[dacp] Add support for play + stop commands

These commands are not used by Remote, but some Airplay speakers are
able to make them (e.g. my Sony STR-DN1040).

Also prepare being able to set the device volume property.
This commit is contained in:
ejurgensen 2018-05-14 23:09:04 +02:00
parent 05c935a7b7
commit 646bf37f17
3 changed files with 87 additions and 31 deletions

View File

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

View File

@ -110,6 +110,8 @@ dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *sta
static void
dacp_propset_volume(const char *value, struct evkeyvalq *query);
static void
dacp_propset_devicevolume(const char *value, struct evkeyvalq *query);
static void
dacp_propset_playingtime(const char *value, struct evkeyvalq *query);
static void
dacp_propset_shufflestate(const char *value, struct evkeyvalq *query);
@ -540,6 +542,26 @@ speaker_enum_cb(struct spk_info *spk, void *arg)
dmap_add_int(evbuf, "cmvo", spk->relvol); /* 12 */
}
static void
seek_timer_cb(int fd, short what, void *arg)
{
int ret;
DPRINTF(E_DBG, L_DACP, "Seek timer expired, target %d ms\n", seek_target);
ret = player_playback_seek(seek_target);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Player failed to seek to %d ms\n", seek_target);
return;
}
ret = player_playback_start();
if (ret < 0)
DPRINTF(E_LOG, L_DACP, "Player returned an error for start after seek\n");
}
static int
dacp_request_authorize(struct httpd_request *hreq)
{
@ -930,23 +952,29 @@ dacp_propset_volume(const char *value, struct evkeyvalq *query)
}
static void
seek_timer_cb(int fd, short what, void *arg)
dacp_propset_devicevolume(const char *value, struct evkeyvalq *query)
{
int ret;
float raop_volume;
int volume;
DPRINTF(E_DBG, L_DACP, "Seek timer expired, target %d ms\n", seek_target);
raop_volume = atof(value);
ret = player_playback_seek(seek_target);
if (ret < 0)
// Basic sanity check, don't want to set to max volume on invalid volume
if (raop_volume == 0.0 && value[0] != '0')
{
DPRINTF(E_LOG, L_DACP, "Player failed to seek to %d ms\n", seek_target);
DPRINTF(E_LOG, L_DACP, "dmcp.device-volume is invalid: %s\n", value);
return;
}
ret = player_playback_start();
if (ret < 0)
DPRINTF(E_LOG, L_DACP, "Player returned an error for start after seek\n");
// RAOP volume: -144.0 is off, -30.0 - 0 maps to 0 - 100
if (raop_volume > -30.0 && raop_volume <= 0.0)
volume = (int)(100.0 * raop_volume / 30.0 + 100.0);
else
volume = 0;
DPRINTF(E_DBG, L_DACP, "Propset volume to %s, new volume is %d\n", value, volume);
// TODO: How to find the id so we can call something like player_volume_speaker_info(id, volume);
}
static void
@ -1312,6 +1340,23 @@ dacp_reply_cue(struct httpd_request *hreq)
}
}
static int
dacp_reply_play(struct httpd_request *hreq)
{
int ret;
ret = dacp_request_authorize(hreq);
if (ret < 0)
return -1;
player_playback_start();
/* 204 No Content is the canonical reply */
httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
return 0;
}
static int
dacp_reply_playspec(struct httpd_request *hreq)
{
@ -1448,6 +1493,23 @@ dacp_reply_playspec(struct httpd_request *hreq)
return -1;
}
static int
dacp_reply_stop(struct httpd_request *hreq)
{
int ret;
ret = dacp_request_authorize(hreq);
if (ret < 0)
return -1;
player_playback_stop();
/* 204 No Content is the canonical reply */
httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP);
return 0;
}
static int
dacp_reply_pause(struct httpd_request *hreq)
{
@ -2376,11 +2438,12 @@ dacp_reply_setproperty(struct httpd_request *hreq)
if (ret < 0)
return -1;
/* Known properties:
* dacp.shufflestate 0/1
* dacp.repeatstate 0/1/2
* dacp.playingtime seek to time in ms
* dmcp.volume 0-100, float
/* Known properties (see dacp_prop.gperf):
* dacp.shufflestate 0/1
* dacp.repeatstate 0/1/2
* dacp.playingtime seek to time in ms
* dmcp.volume 0-100, float
* dmcp.device-volume -144-0, float (raop volume)
*/
/* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */
@ -2535,10 +2598,18 @@ static struct httpd_uri_map dacp_handlers[] =
.regexp = "^/ctrl-int/[[:digit:]]+/cue$",
.handler = dacp_reply_cue
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/play$",
.handler = dacp_reply_play
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/playspec$",
.handler = dacp_reply_playspec
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/stop$",
.handler = dacp_reply_stop
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/pause$",
.handler = dacp_reply_pause

View File

@ -2370,22 +2370,6 @@ raop_metadata_send(void *metadata, uint64_t rtptime, uint64_t offset, int startu
/* Volume handling */
/* For future use
static int
raop_volume_to_pct(float raop_volume)
{
int volume;
// RAOP volume: -144.0 is off, -30.0 - 0 maps to 0 - 100
if (raop_volume > -30.0 && raop_volume <= 0.0)
volume = (int)(100.0 * raop_volume / 30.0 + 100.0);
else
volume = 0;
return volume;
}
*/
static float
raop_volume_from_pct(int volume, char *name)
{