[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.device-volume", NULL, dacp_propset_devicevolume
"dmcp.device-prevent-playback", NULL, dacp_propset_devicepreventplayback
"dacp.playerstate", dacp_propget_playerstate, NULL
"dacp.nowplaying", dacp_propget_nowplaying, NULL
"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
dacp_propset_devicevolume(const char *value, struct httpd_request *hreq);
static void
dacp_propset_devicepreventplayback(const char *value, struct httpd_request *hreq);
static void
dacp_propset_playingtime(const char *value, struct httpd_request *hreq);
static void
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)
{
struct evkeyvalq *headers;
struct player_speaker_info spk_info;
const char *remote;
uint32_t id;
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, &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);
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
@ -2438,6 +2489,7 @@ dacp_reply_setproperty(struct httpd_request *hreq)
* dacp.playingtime seek to time in ms
* dmcp.volume 0-100, float
* dmcp.device-volume -144-0, float (raop volume)
* dmcp.device-prevent-playback 0/1
*/
/* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */

View File

@ -113,12 +113,8 @@
//#define DEBUG_PLAYER 1
struct volume_param {
int volume;
uint64_t spk_id;
};
struct activeremote_param {
uint32_t activeremote;
int volume;
const char *value;
};
@ -137,6 +133,7 @@ struct speaker_set_param
struct speaker_get_param
{
uint64_t spk_id;
uint32_t active_remote;
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));
spk->id = device->id;
spk->active_remote = (uint32_t)device->id;
strncpy(spk->name, device->name, sizeof(spk->name));
spk->name[sizeof(spk->name) - 1] = '\0';
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;
}
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
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)
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;
uint32_t activeremote;
int volume;
*retval = 0;
activeremote = ar_param->activeremote;
for (device = output_device_list; device; device = device->next)
{
if ((uint32_t)device->id == activeremote)
break;
}
device = outputs_device_get(vol_param->spk_id);
if (!device)
{
DPRINTF(E_LOG, L_DACP, "Could not find speaker with Active-Remote id %d\n", activeremote);
*retval = -1;
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)
{
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;
return COMMAND_END;
}
@ -2811,6 +2820,7 @@ volume_byactiveremote(void *arg, int *retval)
listener_notify(LISTENER_VOLUME);
*retval = 0;
return COMMAND_END;
}
@ -3111,6 +3121,19 @@ player_speaker_get_byid(uint64_t id, struct player_speaker_info *spk)
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
player_speaker_enable(uint64_t id)
{
@ -3192,15 +3215,15 @@ player_volume_setabs_speaker(uint64_t id, int vol)
}
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;
ar_param.activeremote = activeremote;
ar_param.value = value;
vol_param.spk_id = id;
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;
}

View File

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