[dacp] Support for dmcp.device-prevent-playback (issue #855)

This commit is contained in:
ejurgensen 2020-01-02 17:48:40 +01:00
parent 80b183c7ff
commit af1bc27dd5
4 changed files with 114 additions and 34 deletions

View File

@ -12,6 +12,7 @@ struct dacp_prop_map;
%% %%
"dmcp.volume", dacp_propget_volume, dacp_propset_volume "dmcp.volume", dacp_propget_volume, dacp_propset_volume
"dmcp.device-volume", NULL, dacp_propset_devicevolume "dmcp.device-volume", NULL, dacp_propset_devicevolume
"dmcp.device-prevent-playback", NULL, dacp_propset_devicepreventplayback
"dacp.playerstate", dacp_propget_playerstate, NULL "dacp.playerstate", dacp_propget_playerstate, NULL
"dacp.nowplaying", dacp_propget_nowplaying, NULL "dacp.nowplaying", dacp_propget_nowplaying, NULL
"dacp.playingtime", dacp_propget_playingtime, dacp_propset_playingtime "dacp.playingtime", dacp_propget_playingtime, dacp_propset_playingtime

View File

@ -112,6 +112,8 @@ dacp_propset_volume(const char *value, struct httpd_request *hreq);
static void static void
dacp_propset_devicevolume(const char *value, struct httpd_request *hreq); dacp_propset_devicevolume(const char *value, struct httpd_request *hreq);
static void static void
dacp_propset_devicepreventplayback(const char *value, struct httpd_request *hreq);
static void
dacp_propset_playingtime(const char *value, struct httpd_request *hreq); dacp_propset_playingtime(const char *value, struct httpd_request *hreq);
static void static void
dacp_propset_shufflestate(const char *value, struct httpd_request *hreq); dacp_propset_shufflestate(const char *value, struct httpd_request *hreq);
@ -957,19 +959,68 @@ static void
dacp_propset_devicevolume(const char *value, struct httpd_request *hreq) dacp_propset_devicevolume(const char *value, struct httpd_request *hreq)
{ {
struct evkeyvalq *headers; struct evkeyvalq *headers;
struct player_speaker_info spk_info;
const char *remote; const char *remote;
uint32_t id; uint32_t active_remote;
int ret;
headers = evhttp_request_get_input_headers(hreq->req); headers = evhttp_request_get_input_headers(hreq->req);
remote = evhttp_find_header(headers, "Active-Remote"); remote = evhttp_find_header(headers, "Active-Remote");
if (!headers || !remote || (safe_atou32(remote, &id) < 0)) if (!headers || !remote || (safe_atou32(remote, &active_remote) < 0))
{ {
DPRINTF(E_LOG, L_DACP, "Request for setting device-volume has invalid Active-Remote: '%s'\n", remote); DPRINTF(E_LOG, L_DACP, "Request for setting device-volume has invalid Active-Remote: '%s'\n", remote);
return; return;
} }
player_volume_byactiveremote(id, value); ret = player_speaker_get_byactiveremote(&spk_info, active_remote);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Request for setting device-volume from unknown Active-Remote: '%s'\n", remote);
return;
}
player_volume_update_speaker(spk_info.id, value);
}
// iTunes seems to use this as way for a speaker to tell the server that it is
// busy with something else. If the speaker makes the request with the value 1,
// then iTunes will disable the speaker, and if it is the only speaker, then
// playback will also be paused. It is not possible for the user to restart the
// speaker until it has made a request with value 0 (if attempted, iTunes will
// show it is waiting for the speaker). As you can see from the below, we
// don't fully match this behaviour, instead we just enable/disable.
static void
dacp_propset_devicepreventplayback(const char *value, struct httpd_request *hreq)
{
struct evkeyvalq *headers;
struct player_speaker_info spk_info;
const char *remote;
uint32_t active_remote;
int ret;
headers = evhttp_request_get_input_headers(hreq->req);
remote = evhttp_find_header(headers, "Active-Remote");
if (!headers || !remote || (safe_atou32(remote, &active_remote) < 0))
{
DPRINTF(E_LOG, L_DACP, "Request for setting device-prevent-playback has invalid Active-Remote: '%s'\n", remote);
return;
}
ret = player_speaker_get_byactiveremote(&spk_info, active_remote);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Request for setting device-prevent-playback from unknown Active-Remote: '%s'\n", remote);
return;
}
if (value[0] == '1')
player_speaker_disable(spk_info.id);
else if (value[0] == '0')
player_speaker_enable(spk_info.id);
else
DPRINTF(E_LOG, L_DACP, "Request for setting device-prevent-playback has invalid value: '%s'\n", value);
} }
static void static void
@ -2433,11 +2484,12 @@ dacp_reply_setproperty(struct httpd_request *hreq)
return -1; return -1;
/* Known properties (see dacp_prop.gperf): /* Known properties (see dacp_prop.gperf):
* dacp.shufflestate 0/1 * dacp.shufflestate 0/1
* dacp.repeatstate 0/1/2 * dacp.repeatstate 0/1/2
* dacp.playingtime seek to time in ms * dacp.playingtime seek to time in ms
* dmcp.volume 0-100, float * dmcp.volume 0-100, float
* dmcp.device-volume -144-0, float (raop volume) * dmcp.device-volume -144-0, float (raop volume)
* dmcp.device-prevent-playback 0/1
*/ */
/* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */ /* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */

View File

@ -113,12 +113,8 @@
//#define DEBUG_PLAYER 1 //#define DEBUG_PLAYER 1
struct volume_param { struct volume_param {
int volume;
uint64_t spk_id; uint64_t spk_id;
}; int volume;
struct activeremote_param {
uint32_t activeremote;
const char *value; const char *value;
}; };
@ -137,6 +133,7 @@ struct speaker_set_param
struct speaker_get_param struct speaker_get_param
{ {
uint64_t spk_id; uint64_t spk_id;
uint32_t active_remote;
struct player_speaker_info *spk_info; struct player_speaker_info *spk_info;
}; };
@ -2392,6 +2389,7 @@ device_to_speaker_info(struct player_speaker_info *spk, struct output_device *de
{ {
memset(spk, 0, sizeof(struct player_speaker_info)); memset(spk, 0, sizeof(struct player_speaker_info));
spk->id = device->id; spk->id = device->id;
spk->active_remote = (uint32_t)device->id;
strncpy(spk->name, device->name, sizeof(spk->name)); strncpy(spk->name, device->name, sizeof(spk->name));
spk->name[sizeof(spk->name) - 1] = '\0'; spk->name[sizeof(spk->name) - 1] = '\0';
strncpy(spk->output_type, device->type_name, sizeof(spk->output_type)); strncpy(spk->output_type, device->type_name, sizeof(spk->output_type));
@ -2445,6 +2443,27 @@ speaker_get_byid(void *arg, int *retval)
return COMMAND_END; return COMMAND_END;
} }
static enum command_state
speaker_get_byactiveremote(void *arg, int *retval)
{
struct speaker_get_param *spk_param = arg;
struct output_device *device;
for (device = output_device_list; device; device = device->next)
{
if ((uint32_t)device->id == spk_param->active_remote)
{
device_to_speaker_info(spk_param->spk_info, device);
*retval = 0;
return COMMAND_END;
}
}
// No output device found with matching id
*retval = -1;
return COMMAND_END;
}
static int static int
speaker_activate(struct output_device *device) speaker_activate(struct output_device *device)
{ {
@ -2770,33 +2789,23 @@ volume_setabs_speaker(void *arg, int *retval)
// Just updates internal volume params (does not make actual requests to the speaker) // Just updates internal volume params (does not make actual requests to the speaker)
static enum command_state static enum command_state
volume_byactiveremote(void *arg, int *retval) volume_update_speaker(void *arg, int *retval)
{ {
struct activeremote_param *ar_param = arg; struct volume_param *vol_param = arg;
struct output_device *device; struct output_device *device;
uint32_t activeremote;
int volume; int volume;
*retval = 0; device = outputs_device_get(vol_param->spk_id);
activeremote = ar_param->activeremote;
for (device = output_device_list; device; device = device->next)
{
if ((uint32_t)device->id == activeremote)
break;
}
if (!device) if (!device)
{ {
DPRINTF(E_LOG, L_DACP, "Could not find speaker with Active-Remote id %d\n", activeremote);
*retval = -1; *retval = -1;
return COMMAND_END; return COMMAND_END;
} }
volume = outputs_device_volume_to_pct(device, ar_param->value); // Only converts volume = outputs_device_volume_to_pct(device, vol_param->value); // Only converts
if (volume < 0) if (volume < 0)
{ {
DPRINTF(E_LOG, L_DACP, "Could not parse volume given by Active-Remote id %d\n", activeremote); DPRINTF(E_LOG, L_DACP, "Could not parse volume '%s' in update_volume() for speaker '%s'\n", vol_param->value, device->name);
*retval = -1; *retval = -1;
return COMMAND_END; return COMMAND_END;
} }
@ -2811,6 +2820,7 @@ volume_byactiveremote(void *arg, int *retval)
listener_notify(LISTENER_VOLUME); listener_notify(LISTENER_VOLUME);
*retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -3111,6 +3121,19 @@ player_speaker_get_byid(uint64_t id, struct player_speaker_info *spk)
return ret; return ret;
} }
int
player_speaker_get_byactiveremote(struct player_speaker_info *spk, uint32_t active_remote)
{
struct speaker_get_param param;
int ret;
param.active_remote = active_remote;
param.spk_info = spk;
ret = commands_exec_sync(cmdbase, speaker_get_byactiveremote, NULL, &param);
return ret;
}
int int
player_speaker_enable(uint64_t id) player_speaker_enable(uint64_t id)
{ {
@ -3192,15 +3215,15 @@ player_volume_setabs_speaker(uint64_t id, int vol)
} }
int int
player_volume_byactiveremote(uint32_t activeremote, const char *value) player_volume_update_speaker(uint64_t id, const char *value)
{ {
struct activeremote_param ar_param; struct volume_param vol_param;
int ret; int ret;
ar_param.activeremote = activeremote; vol_param.spk_id = id;
ar_param.value = value; vol_param.value = value;
ret = commands_exec_sync(cmdbase, volume_byactiveremote, NULL, &ar_param); ret = commands_exec_sync(cmdbase, volume_update_speaker, NULL, &vol_param);
return ret; return ret;
} }

View File

@ -29,6 +29,7 @@ enum player_seek_mode {
struct player_speaker_info { struct player_speaker_info {
uint64_t id; uint64_t id;
uint32_t active_remote;
char name[255]; char name[255];
char output_type[50]; char output_type[50];
int relvol; int relvol;
@ -92,6 +93,9 @@ player_speaker_set(uint64_t *ids);
int int
player_speaker_get_byid(uint64_t id, struct player_speaker_info *spk); player_speaker_get_byid(uint64_t id, struct player_speaker_info *spk);
int
player_speaker_get_byactiveremote(struct player_speaker_info *spk, uint32_t active_remote);
int int
player_speaker_enable(uint64_t id); player_speaker_enable(uint64_t id);
@ -132,7 +136,7 @@ int
player_volume_setabs_speaker(uint64_t id, int vol); player_volume_setabs_speaker(uint64_t id, int vol);
int int
player_volume_byactiveremote(uint32_t activeremote, const char *value); player_volume_update_speaker(uint64_t id, const char *value);
int int
player_repeat_set(enum repeat_mode mode); player_repeat_set(enum repeat_mode mode);