2011-10-03 22:01:14

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 1/5] Fix not being able to register pdu handlers

From: Luiz Augusto von Dentz <[email protected]>

This happens when removing and adding again an adapter
---
audio/avctp.c | 15 +++++++++++----
1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index b8cb36e..89ef70c 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -463,6 +463,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,

handler = find_handler(handlers, avc->opcode);
if (!handler) {
+ DBG("handler not found for 0x%02x", avc->opcode);
avc->code = AVC_CTYPE_REJECTED;
goto done;
}
@@ -824,14 +825,20 @@ void avctp_unregister(const bdaddr_t *src)
if (servers)
return;

- if (passthrough_id)
+ if (passthrough_id) {
avctp_unregister_pdu_handler(passthrough_id);
+ passthrough_id = 0;
+ }

- if (unit_id)
+ if (unit_id) {
avctp_unregister_pdu_handler(unit_id);
+ passthrough_id = 0;
+ }

- if (subunit_id)
- avctp_unregister_pdu_handler(unit_id);
+ if (subunit_id) {
+ avctp_unregister_pdu_handler(subunit_id);
+ subunit_id = 0;
+ }
}

int avctp_send_passthrough(struct avctp *session, uint8_t op)
--
1.7.6.4



2011-10-04 00:32:38

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH BlueZ 2/5] AVRCP: move MediaPlayer to adapter object

* Luiz Augusto von Dentz <[email protected]> [2011-10-04 01:01:15 +0300]:

> From: Luiz Augusto von Dentz <[email protected]>
>
> This move the MediaPlayer registration to adapter object on Media
> interface so we can track players properly.
> ---
> audio/avrcp.c | 832 +++++++++++------------------------------------------
> audio/avrcp.h | 67 ++++-
> audio/device.c | 3 -
> audio/device.h | 2 -
> audio/manager.c | 7 -
> audio/media.c | 776 +++++++++++++++++++++++++++++++++++++++++++++++++
> doc/media-api.txt | 163 +++++++++++
> 7 files changed, 1167 insertions(+), 683 deletions(-)
>
> diff --git a/audio/avrcp.c b/audio/avrcp.c
> index 6e8b8ff..cfdb313 100644
> --- a/audio/avrcp.c
> +++ b/audio/avrcp.c
> @@ -78,54 +78,10 @@
> #define AVRCP_GET_PLAY_STATUS 0x30
> #define AVRCP_REGISTER_NOTIFICATION 0x31
>
> -/* Notification events */
> -#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED 0x01
> -#define AVRCP_EVENT_TRACK_CHANGED 0x02
> -
> /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
> #define CAP_COMPANY_ID 0x02
> #define CAP_EVENTS_SUPPORTED 0x03
>
> -enum player_setting {
> - PLAYER_SETTING_EQUALIZER = 1,
> - PLAYER_SETTING_REPEAT = 2,
> - PLAYER_SETTING_SHUFFLE = 3,
> - PLAYER_SETTING_SCAN = 4,
> -};
> -
> -enum equalizer_mode {
> - EQUALIZER_MODE_OFF = 1,
> - EQUALIZER_MODE_ON = 2,
> -};
> -
> -enum repeat_mode {
> - REPEAT_MODE_OFF = 1,
> - REPEAT_MODE_SINGLE = 2,
> - REPEAT_MODE_ALL = 3,
> - REPEAT_MODE_GROUP = 4,
> -};
> -
> -enum shuffle_mode {
> - SHUFFLE_MODE_OFF = 1,
> - SHUFFLE_MODE_ALL = 2,
> - SHUFFLE_MODE_GROUP = 3,
> -};
> -
> -enum scan_mode {
> - SCAN_MODE_OFF = 1,
> - SCAN_MODE_ALL = 2,
> - SCAN_MODE_GROUP = 3,
> -};
> -
> -enum play_status {
> - PLAY_STATUS_STOPPED = 0x00,
> - PLAY_STATUS_PLAYING = 0x01,
> - PLAY_STATUS_PAUSED = 0x02,
> - PLAY_STATUS_FWD_SEEK = 0x03,
> - PLAY_STATUS_REV_SEEK = 0x04,
> - PLAY_STATUS_ERROR = 0xFF
> -};
> -
> enum battery_status {
> BATTERY_STATUS_NORMAL = 0,
> BATTERY_STATUS_WARNING = 1,
> @@ -134,17 +90,6 @@ enum battery_status {
> BATTERY_STATUS_FULL_CHARGE = 4,
> };
>
> -enum media_info_id {
> - MEDIA_INFO_TITLE = 1,
> - MEDIA_INFO_ARTIST = 2,
> - MEDIA_INFO_ALBUM = 3,
> - MEDIA_INFO_TRACK = 4,
> - MEDIA_INFO_N_TRACKS = 5,
> - MEDIA_INFO_GENRE = 6,
> - MEDIA_INFO_PLAYING_TIME = 7,
> - MEDIA_INFO_LAST
> -};
> -
> #if __BYTE_ORDER == __LITTLE_ENDIAN
>
> struct avrcp_header {
> @@ -180,33 +125,26 @@ struct avrcp_server {
> bdaddr_t src;
> uint32_t tg_record_id;
> uint32_t ct_record_id;
> + GSList *players;
> + struct avrcp_player *active_player;
> };
>
> -struct media_info {
> - char *title;
> - char *artist;
> - char *album;
> - char *genre;
> - uint32_t ntracks;
> - uint32_t track;
> - uint32_t track_len;
> - uint32_t elapsed;
> -};
> -
> -struct media_player {
> +struct avrcp_player {
> + struct avrcp_server *server;
> struct avctp *session;
> struct audio_device *dev;
> - uint8_t settings[PLAYER_SETTING_SCAN + 1];
> - enum play_status status;
>
> - struct media_info mi;
> - GTimer *timer;
> unsigned int handler;
> uint16_t registered_events;
> uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
> +
> + struct avrcp_player_cb *cb;
> + void *user_data;
> + GDestroyNotify destroy;
> };
>
> static GSList *servers = NULL;
> +static unsigned int avctp_id = 0;
>
> /* Company IDs supported by this device */
> static uint32_t company_ids[] = {
> @@ -344,164 +282,19 @@ static sdp_record_t *avrcp_tg_record(void)
> static unsigned int attr_get_max_val(uint8_t attr)
> {
> switch (attr) {
> - case PLAYER_SETTING_EQUALIZER:
> - return EQUALIZER_MODE_ON;
> - case PLAYER_SETTING_REPEAT:
> - return REPEAT_MODE_GROUP;
> - case PLAYER_SETTING_SHUFFLE:
> - return SHUFFLE_MODE_GROUP;
> - case PLAYER_SETTING_SCAN:
> - return SCAN_MODE_GROUP;
> + case AVRCP_ATTRIBUTE_EQUALIZER:
> + return AVRCP_EQUALIZER_ON;
> + case AVRCP_ATTRIBUTE_REPEAT_MODE:
> + return AVRCP_REPEAT_MODE_GROUP;
> + case AVRCP_ATTRIBUTE_SHUFFLE:
> + return AVRCP_SHUFFLE_GROUP;
> + case AVRCP_ATTRIBUTE_SCAN:
> + return AVRCP_SCAN_GROUP;
> }
>
> return 0;
> }
>
> -static const char *attrval_to_str(uint8_t attr, uint8_t value)
> -{
> - switch (attr) {
> - case PLAYER_SETTING_EQUALIZER:
> - switch (value) {
> - case EQUALIZER_MODE_ON:
> - return "on";
> - case EQUALIZER_MODE_OFF:
> - return "off";
> - }
> -
> - break;
> - case PLAYER_SETTING_REPEAT:
> - switch (value) {
> - case REPEAT_MODE_OFF:
> - return "off";
> - case REPEAT_MODE_SINGLE:
> - return "singletrack";
> - case REPEAT_MODE_ALL:
> - return "alltracks";
> - case REPEAT_MODE_GROUP:
> - return "group";
> - }
> -
> - break;
> - /* Shuffle and scan have the same values */
> - case PLAYER_SETTING_SHUFFLE:
> - case PLAYER_SETTING_SCAN:
> - switch (value) {
> - case SCAN_MODE_OFF:
> - return "off";
> - case SCAN_MODE_ALL:
> - return "alltracks";
> - case SCAN_MODE_GROUP:
> - return "group";
> - }
> -
> - break;
> - }
> -
> - return NULL;
> -}
> -
> -static int attrval_to_val(uint8_t attr, const char *value)
> -{
> - int ret;
> -
> - switch (attr) {
> - case PLAYER_SETTING_EQUALIZER:
> - if (!strcmp(value, "off"))
> - ret = EQUALIZER_MODE_OFF;
> - else if (!strcmp(value, "on"))
> - ret = EQUALIZER_MODE_ON;
> - else
> - ret = -EINVAL;
> -
> - return ret;
> - case PLAYER_SETTING_REPEAT:
> - if (!strcmp(value, "off"))
> - ret = REPEAT_MODE_OFF;
> - else if (!strcmp(value, "singletrack"))
> - ret = REPEAT_MODE_SINGLE;
> - else if (!strcmp(value, "alltracks"))
> - ret = REPEAT_MODE_ALL;
> - else if (!strcmp(value, "group"))
> - ret = REPEAT_MODE_GROUP;
> - else
> - ret = -EINVAL;
> -
> - return ret;
> - case PLAYER_SETTING_SHUFFLE:
> - if (!strcmp(value, "off"))
> - ret = SHUFFLE_MODE_OFF;
> - else if (!strcmp(value, "alltracks"))
> - ret = SHUFFLE_MODE_ALL;
> - else if (!strcmp(value, "group"))
> - ret = SHUFFLE_MODE_GROUP;
> - else
> - ret = -EINVAL;
> -
> - return ret;
> - case PLAYER_SETTING_SCAN:
> - if (!strcmp(value, "off"))
> - ret = SCAN_MODE_OFF;
> - else if (!strcmp(value, "alltracks"))
> - ret = SCAN_MODE_ALL;
> - else if (!strcmp(value, "group"))
> - ret = SCAN_MODE_GROUP;
> - else
> - ret = -EINVAL;
> -
> - return ret;
> - }
> -
> - return -EINVAL;
> -}
> -
> -static const char *attr_to_str(uint8_t attr)
> -{
> - switch (attr) {
> - case PLAYER_SETTING_EQUALIZER:
> - return "Equalizer";
> - case PLAYER_SETTING_REPEAT:
> - return "Repeat";
> - case PLAYER_SETTING_SHUFFLE:
> - return "Shuffle";
> - case PLAYER_SETTING_SCAN:
> - return "Scan";
> - }
> -
> - return NULL;
> -}
> -
> -static int attr_to_val(const char *str)
> -{
> - if (!strcmp(str, "Equalizer"))
> - return PLAYER_SETTING_EQUALIZER;
> - else if (!strcmp(str, "Repeat"))
> - return PLAYER_SETTING_REPEAT;
> - else if (!strcmp(str, "Shuffle"))
> - return PLAYER_SETTING_SHUFFLE;
> - else if (!strcmp(str, "Scan"))
> - return PLAYER_SETTING_SCAN;
> -
> - return -EINVAL;
> -}
> -
> -static int play_status_to_val(const char *status)
> -{
> - if (!strcmp(status, "stopped"))
> - return PLAY_STATUS_STOPPED;
> - else if (!strcmp(status, "playing"))
> - return PLAY_STATUS_PLAYING;
> - else if (!strcmp(status, "paused"))
> - return PLAY_STATUS_PAUSED;
> - else if (!strcmp(status, "forward-seek"))
> - return PLAY_STATUS_FWD_SEEK;
> - else if (!strcmp(status, "reverse-seek"))
> - return PLAY_STATUS_REV_SEEK;
> - else if (!strcmp(status, "error"))
> - return PLAY_STATUS_ERROR;
> -
> - return -EINVAL;
> -}
> -
> static const char *battery_status_to_str(enum battery_status status)
> {
> switch (status) {
> @@ -542,17 +335,17 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
> cid[2] = cid_in;
> }
>
> -static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
> +int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
> {
> uint8_t buf[AVRCP_HEADER_LENGTH + 9];
> struct avrcp_header *pdu = (void *) buf;
> uint16_t size;
> int err;
>
> - if (mp->session == NULL)
> + if (player->session == NULL)
> return -ENOTCONN;
>
> - if (!(mp->registered_events & (1 << id)))
> + if (!(player->registered_events & (1 << id)))
> return 0;
>
> memset(buf, 0, sizeof(buf));
> @@ -565,7 +358,7 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
> DBG("id=%u", id);
>
> switch (id) {
> - case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
> + case AVRCP_EVENT_STATUS_CHANGED:
> size = 2;
> pdu->params[1] = *((uint8_t *)data);
>
> @@ -589,58 +382,18 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
>
> pdu->params_len = htons(size);
>
> - err = avctp_send_vendordep(mp->session, mp->transaction_events[id],
> + err = avctp_send_vendordep(player->session, player->transaction_events[id],
> AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
> buf, size + AVRCP_HEADER_LENGTH);
> if (err < 0)
> return err;
>
> /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
> - mp->registered_events ^= 1 << id;
> + player->registered_events ^= 1 << id;
>
> return 0;
> }
>
> -static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
> - uint32_t *elapsed, uint32_t *track_len)
> -{
> - if (status)
> - *status = mp->status;
> - if (track_len)
> - *track_len = mp->mi.track_len;
> -
> - if (!elapsed)
> - return;
> -
> - *elapsed = mp->mi.elapsed;
> -
> - if (mp->status == PLAY_STATUS_PLAYING) {
> - double timedelta = g_timer_elapsed(mp->timer, NULL);
> - uint32_t sec, msec;
> -
> - sec = (uint32_t) timedelta;
> - msec = (uint32_t)((timedelta - sec) * 1000);
> -
> - *elapsed += sec * 1000 + msec;
> - }
> -}
> -
> -static void mp_set_playback_status(struct media_player *mp, uint8_t status,
> - uint32_t elapsed)
> -{
> - DBG("Change playback: %u %u", status, elapsed);
> -
> - mp->mi.elapsed = elapsed;
> - g_timer_start(mp->timer);
> -
> - if (status == mp->status)
> - return;
> -
> - mp->status = status;
> -
> - avrcp_send_event(mp, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED, &status);
> -}
> -
> /*
> * Copy media_info field to a buffer, intended to be used in a response to
> * GetElementAttributes message.
> @@ -648,10 +401,10 @@ static void mp_set_playback_status(struct media_player *mp, uint8_t status,
> * It assumes there's enough space in the buffer and on success it returns the
> * size written.
> *
> - * If @param id is not valid, -EINVAL is returned. If there's no such media
> - * attribute, -ENOENT is returned.
> + * If @param id is not valid, -EINVAL is returned. If there's no space left on
> + * the buffer -ENOBUFS is returned.
> */
> -static int mp_get_media_attribute(struct media_player *mp,
> +static int player_get_media_attribute(struct avrcp_player *player,
> uint32_t id, uint8_t *buf,
> uint16_t maxlen)
> {
> @@ -661,11 +414,10 @@ static int mp_get_media_attribute(struct media_player *mp,
> uint16_t len;
> uint8_t val[];
> };
> - const struct media_info *mi = &mp->mi;
> struct media_info_elem *elem = (void *)buf;
> uint16_t len;
> char valstr[20];
> - char *valp;
> + void *value;
>
> if (maxlen < sizeof(struct media_info_elem))
> return -ENOBUFS;
> @@ -673,61 +425,38 @@ static int mp_get_media_attribute(struct media_player *mp,
> /* Subtract the size of elem header from the available space */
> maxlen -= sizeof(struct media_info_elem);
>
> - switch (id) {
> - case MEDIA_INFO_TITLE:
> - valp = mi->title;
> - break;
> - case MEDIA_INFO_ARTIST:
> - valp = mi->artist;
> - break;
> - case MEDIA_INFO_ALBUM:
> - valp = mi->album;
> - break;
> - case MEDIA_INFO_GENRE:
> - valp = mi->genre;
> - break;
> - case MEDIA_INFO_TRACK:
> - if (mi->track) {
> - snprintf(valstr, 20, "%u", mi->track);
> - valp = valstr;
> - } else {
> - valp = NULL;
> - }
> + DBG("Get media attribute: %u", id);
>
> - break;
> - case MEDIA_INFO_N_TRACKS:
> - if (mi->ntracks) {
> - snprintf(valstr, 20, "%u", mi->ntracks);
> - valp = valstr;
> - } else {
> - valp = NULL;
> - }
> + value = player->cb->get_metadata(id, player->user_data);
> + if (value == NULL) {
> + len = 0;
> + goto done;
> + }
>
> + switch (id) {
> + case AVRCP_MEDIA_ATTRIBUTE_TITLE:
> + case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
> + case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
> + case AVRCP_MEDIA_ATTRIBUTE_GENRE:
> + len = strlen((char *) value);
> + if (len > maxlen)
> + return -ENOBUFS;
> + memcpy(elem->val, value, len);
> break;
> - case MEDIA_INFO_PLAYING_TIME:
> - if (mi->track_len == 0xFFFFFFFF) {
> - snprintf(valstr, 20, "%u", mi->track_len);
> - valp = valstr;
> - } else {
> - valp = NULL;
> - }
> -
> + case AVRCP_MEDIA_ATTRIBUTE_TRACK:
> + case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
> + case AVRCP_MEDIA_ATTRIBUTE_DURATION:
> + snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value));
> + len = strlen(valstr);
> + if (len > maxlen)
> + return -ENOBUFS;
> + memcpy(elem->val, valstr, len);
> break;
> default:
> return -ENOENT;
> }
>
> - if (valp) {
> - len = strlen(valp);
> -
> - if (len > maxlen)
> - return -ENOBUFS;
> -
> - memcpy(elem->val, valp, len);
> - } else {
> - len = 0;
> - }
> -
> +done:
> elem->id = htonl(id);
> elem->charset = htons(0x6A); /* Always use UTF-8 */
> elem->len = htons(len);
> @@ -735,57 +464,22 @@ static int mp_get_media_attribute(struct media_player *mp,
> return sizeof(struct media_info_elem) + len;
> }
>
> -static void mp_set_attribute(struct media_player *mp,
> +static int player_set_attribute(struct avrcp_player *player,
> uint8_t attr, uint8_t val)
> {
> DBG("Change attribute: %u %u", attr, val);
>
> - mp->settings[attr] = val;
> + return player->cb->set_setting(attr, val, player->user_data);
> }
>
> -static int mp_get_attribute(struct media_player *mp, uint8_t attr)
> +static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
> {
> DBG("Get attribute: %u", attr);
>
> - return mp->settings[attr];
> -}
> -
> -static void mp_set_media_attributes(struct media_player *mp,
> - struct media_info *mi)
> -{
> - g_free(mp->mi.title);
> - mp->mi.title = g_strdup(mi->title);
> -
> - g_free(mp->mi.artist);
> - mp->mi.artist = g_strdup(mi->artist);
> -
> - g_free(mp->mi.album);
> - mp->mi.album = g_strdup(mi->album);
> -
> - g_free(mp->mi.genre);
> - mp->mi.genre = g_strdup(mi->genre);
> -
> - mp->mi.ntracks = mi->ntracks;
> - mp->mi.track = mi->track;
> - mp->mi.track_len = mi->track_len;
> -
> - /*
> - * elapsed is special. Whenever the track changes, we reset it to 0,
> - * so client doesn't have to make another call to change_playback
> - */
> - mp->mi.elapsed = 0;
> - g_timer_start(mp->timer);
> -
> - DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
> - "\tgenre: %s\n\tNumber of tracks: %u\n"
> - "\tTrack number: %u\n\tTrack duration: %u",
> - mi->title, mi->artist, mi->album, mi->genre,
> - mi->ntracks, mi->track, mi->track_len);
> -
> - avrcp_send_event(mp, AVRCP_EVENT_TRACK_CHANGED, NULL);
> + return player->cb->get_setting(attr, player->user_data);
> }
>
> -static uint8_t avrcp_handle_get_capabilities(struct media_player *mp,
> +static uint8_t avrcp_handle_get_capabilities(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -811,7 +505,7 @@ static uint8_t avrcp_handle_get_capabilities(struct media_player *mp,
> case CAP_EVENTS_SUPPORTED:
> pdu->params_len = htons(4);
> pdu->params[1] = 2;
> - pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
> + pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
> pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
>
> return AVC_CTYPE_STABLE;
> @@ -824,7 +518,7 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp,
> +static uint8_t avrcp_handle_list_player_attributes(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -837,11 +531,11 @@ static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp,
> return AVC_CTYPE_REJECTED;
> }
>
> - if (!mp)
> + if (!player)
> goto done;
>
> - for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
> - if (!mp_get_attribute(mp, i)) {
> + for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
> + if (!player_get_attribute(player, i)) {
> DBG("Ignoring setting %u: not supported by player", i);
> continue;
> }
> @@ -857,14 +551,14 @@ done:
> return AVC_CTYPE_STABLE;
> }
>
> -static uint8_t avrcp_handle_list_player_values(struct media_player *mp,
> +static uint8_t avrcp_handle_list_player_values(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> uint16_t len = ntohs(pdu->params_len);
> unsigned int i;
>
> - if (len != 1 || !mp)
> + if (len != 1 || !player)
> goto err;
>
> len = attr_get_max_val(pdu->params[0]);
> @@ -887,7 +581,7 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
> +static uint8_t avrcp_handle_get_element_attributes(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -910,8 +604,9 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
> * Return all available information, at least
> * title must be returned.
> */
> - for (i = 1; i < MEDIA_INFO_LAST; i++) {
> - size = mp_get_media_attribute(mp, i, &pdu->params[pos],
> + for (i = 1; i < AVRCP_MEDIA_ATTRIBUTE_LAST; i++) {
> + size = player_get_media_attribute(player, i,
> + &pdu->params[pos],
> AVRCP_PDU_MTU - pos);
>
> if (size > 0) {
> @@ -927,7 +622,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
> for (i = 0; i < nattr; i++) {
> uint32_t attr = ntohl(attr_ids[i]);
>
> - size = mp_get_media_attribute(mp, attr,
> + size = player_get_media_attribute(player, attr,
> &pdu->params[pos],
> AVRCP_PDU_MTU - pos);
>
> @@ -953,7 +648,7 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
> +static uint8_t avrcp_handle_get_current_player_value(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -961,7 +656,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
> uint8_t *settings;
> unsigned int i;
>
> - if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
> + if (player == NULL || len <= 1 || pdu->params[0] != len - 1)
> goto err;
>
> /*
> @@ -979,13 +674,13 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
> for (i = 0; i < pdu->params[0]; i++) {
> uint8_t val;
>
> - if (settings[i] < PLAYER_SETTING_EQUALIZER ||
> - settings[i] > PLAYER_SETTING_SCAN) {
> + if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER ||
> + settings[i] > AVRCP_ATTRIBUTE_SCAN) {
> DBG("Ignoring %u", settings[i]);
> continue;
> }
>
> - val = mp_get_attribute(mp, settings[i]);
> + val = player_get_attribute(player, settings[i]);
> if (!val) {
> DBG("Ignoring %u: not supported by player",
> settings[i]);
> @@ -1014,7 +709,7 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
> +static uint8_t avrcp_handle_set_player_value(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -1036,23 +731,11 @@ static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
> for (i = 1; i < pdu->params[0]; i += 2) {
> uint8_t attr = pdu->params[i];
> uint8_t val = pdu->params[i + 1];
> - const char *attrstr;
> - const char *valstr;
> -
> - attrstr = attr_to_str(attr);
> - if (!attrstr)
> - continue;
>
> - valstr = attrval_to_str(attr, val);
> - if (!valstr)
> + if (player_set_attribute(player, attr, val) < 0)
> continue;
>
> len++;
> -
> - mp_set_attribute(mp, attr, val);
> - emit_property_changed(mp->dev->conn, mp->dev->path,
> - MEDIA_PLAYER_INTERFACE, attrstr,
> - DBUS_TYPE_STRING, &valstr);
> }
>
> if (len) {
> @@ -1067,7 +750,7 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_displayable_charset(struct media_player *mp,
> +static uint8_t avrcp_handle_displayable_charset(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -1087,7 +770,7 @@ static uint8_t avrcp_handle_displayable_charset(struct media_player *mp,
> return AVC_CTYPE_STABLE;
> }
>
> -static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp,
> +static uint8_t avrcp_handle_ct_battery_status(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> @@ -1101,9 +784,6 @@ static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp,
> if (valstr == NULL)
> goto err;
>
> - emit_property_changed(mp->dev->conn, mp->dev->path,
> - MEDIA_PLAYER_INTERFACE, "Battery",
> - DBUS_TYPE_STRING, &valstr);
> pdu->params_len = 0;
>
> return AVC_CTYPE_STABLE;
> @@ -1114,14 +794,13 @@ err:
> return AVC_CTYPE_REJECTED;
> }
>
> -static uint8_t avrcp_handle_get_play_status(struct media_player *mp,
> +static uint8_t avrcp_handle_get_play_status(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> uint16_t len = ntohs(pdu->params_len);
> - uint32_t elapsed;
> - uint32_t track_len;
> - uint8_t status;
> + uint32_t position;
> + uint32_t duration;
>
> if (len != 0) {
> pdu->params_len = htons(1);
> @@ -1129,25 +808,28 @@ static uint8_t avrcp_handle_get_play_status(struct media_player *mp,
> return AVC_CTYPE_REJECTED;
> }
>
> - mp_get_playback_status(mp, &status, &elapsed, &track_len);
> - track_len = htonl(track_len);
> - elapsed = htonl(elapsed);
> + position = player->cb->get_position(player->user_data);
> + duration = GPOINTER_TO_UINT(player->cb->get_metadata(
> + AVRCP_MEDIA_ATTRIBUTE_DURATION,
> + player->user_data));
> +
> + duration = htonl(duration);
> + position = htonl(position);
>
> - memcpy(&pdu->params[0], &track_len, 4);
> - memcpy(&pdu->params[4], &elapsed, 4);
> - pdu->params[8] = status;
> + memcpy(&pdu->params[0], &duration, 4);
> + memcpy(&pdu->params[4], &position, 4);
> + pdu->params[8] = player->cb->get_status(player->user_data);;
>
> pdu->params_len = htons(9);
>
> return AVC_CTYPE_STABLE;
> }
>
> -static uint8_t avrcp_handle_register_notification(struct media_player *mp,
> +static uint8_t avrcp_handle_register_notification(struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction)
> {
> uint16_t len = ntohs(pdu->params_len);
> - uint8_t status;
>
> /*
> * 1 byte for EventID, 4 bytes for Playback interval but the latest
> @@ -1158,10 +840,9 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp,
> goto err;
>
> switch (pdu->params[0]) {
> - case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
> + case AVRCP_EVENT_STATUS_CHANGED:
> len = 2;
> - mp_get_playback_status(mp, &status, NULL, NULL);
> - pdu->params[1] = status;
> + pdu->params[1] = player->cb->get_status(player->user_data);
>
> break;
> case AVRCP_EVENT_TRACK_CHANGED:
> @@ -1176,8 +857,8 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp,
> }
>
> /* Register event and save the transaction used */
> - mp->registered_events |= (1 << pdu->params[0]);
> - mp->transaction_events[pdu->params[0]] = transaction;
> + player->registered_events |= (1 << pdu->params[0]);
> + player->transaction_events[pdu->params[0]] = transaction;
>
> pdu->params_len = htons(len);
>
> @@ -1192,7 +873,7 @@ err:
> static struct pdu_handler {
> uint8_t pdu_id;
> uint8_t code;
> - uint8_t (*func) (struct media_player *mp,
> + uint8_t (*func) (struct avrcp_player *player,
> struct avrcp_header *pdu,
> uint8_t transaction);
> } handlers[] = {
> @@ -1229,7 +910,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
> uint8_t *operands, size_t operand_count,
> void *user_data)
> {
> - struct media_player *mp = user_data;
> + struct avrcp_player *player = user_data;
> struct pdu_handler *handler;
> struct avrcp_header *pdu = (void *) operands;
> uint32_t company_id = get_company_id(pdu->company_id);
> @@ -1265,7 +946,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
> goto err_metadata;
> }
>
> - *code = handler->func(mp, pdu, transaction);
> + *code = handler->func(player, pdu, transaction);
>
> return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
>
> @@ -1276,52 +957,56 @@ err_metadata:
> return AVRCP_HEADER_LENGTH + 1;
> }
>
> +static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
> +{
> + for (; list; list = list->next) {
> + struct avrcp_server *server = list->data;
> +
> + if (bacmp(&server->src, src) == 0)
> + return server;
> + }
> +
> + return NULL;
> +}
> +
> static void state_changed(struct audio_device *dev, avctp_state_t old_state,
> avctp_state_t new_state, void *user_data)
> {
> - struct media_player *mp = dev->media_player;
> + struct avrcp_server *server;
> + struct avrcp_player *player;
>
> + server = find_server(servers, &dev->src);
> + if (!server)
> + return;
>
> - if (!mp)
> + player = server->active_player;
> + if (!player)
> return;
>
> switch (new_state) {
> case AVCTP_STATE_DISCONNECTED:
> - mp->session = NULL;
> + player->session = NULL;
>
> - if (mp->handler) {
> - avctp_unregister_pdu_handler(mp->handler);
> - mp->handler = 0;
> + if (player->handler) {
> + avctp_unregister_pdu_handler(player->handler);
> + player->handler = 0;
> }
>
> break;
> case AVCTP_STATE_CONNECTING:
> - mp->session = avctp_connect(&dev->src, &dev->dst);
> + player->session = avctp_connect(&dev->src, &dev->dst);
>
> - if (!mp->handler)
> - mp->handler = avctp_register_pdu_handler(
> + if (!player->handler)
> + player->handler = avctp_register_pdu_handler(
> AVC_OP_VENDORDEP,
> handle_vendordep_pdu,
> - mp);
> + player);
> break;
> default:
> return;
> }
> }
>
> -static void media_info_init(struct media_info *mi)
> -{
> - memset(mi, 0, sizeof(*mi));
> -
> - /*
> - * As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG
> - * does not support these attributes (i.e. they were never set via
> - * D-Bus)
> - */
> - mi->track_len = 0xFFFFFFFF;
> - mi->elapsed = 0xFFFFFFFF;
> -}
> -
> gboolean avrcp_connect(struct audio_device *dev)
> {
> struct avctp *session;
> @@ -1344,8 +1029,6 @@ void avrcp_disconnect(struct audio_device *dev)
> avctp_disconnect(session);
> }
>
> -static unsigned int avctp_id = 0;
> -
> int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
> {
> sdp_record_t *record;
> @@ -1390,7 +1073,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
> }
>
> if (add_record_to_server(src, record) < 0) {
> - error("Unable to register AVRCP mpler service record");
> + error("Unable to register AVRCP service record");
> sdp_record_free(record);
> g_free(server);
> return -1;
> @@ -1411,16 +1094,17 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
> return 0;
> }
>
> -static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
> +static void player_destroy(gpointer data)
> {
> - for (; list; list = list->next) {
> - struct avrcp_server *server = list->data;
> + struct avrcp_player *player = data;
>
> - if (bacmp(&server->src, src) == 0)
> - return server;
> - }
> + if (player->destroy)
> + player->destroy(player->user_data);
>
> - return NULL;
> + if (player->handler)
> + avctp_unregister_pdu_handler(player->handler);
> +
> + g_free(player);
> }
>
> void avrcp_unregister(const bdaddr_t *src)
> @@ -1431,6 +1115,8 @@ void avrcp_unregister(const bdaddr_t *src)
> if (!server)
> return;
>
> + g_slist_free_full(server->players, player_destroy);
> +
> servers = g_slist_remove(servers, server);
>
> remove_record_from_server(server->ct_record_id);
> @@ -1442,239 +1128,49 @@ void avrcp_unregister(const bdaddr_t *src)
> if (servers)
> return;
>
> - if (avctp_id)
> + if (avctp_id) {
> avctp_remove_state_cb(avctp_id);
> -}
> -
> -static DBusMessage *mp_set_property(DBusConnection *conn,
> - DBusMessage *msg, void *data)
> -{
> - struct audio_device *device = data;
> - struct media_player *mp = device->media_player;
> - DBusMessageIter iter;
> - DBusMessageIter var;
> - const char *attrstr, *valstr;
> - int attr, val;
> -
> - if (!dbus_message_iter_init(msg, &iter))
> - return btd_error_invalid_args(msg);
> -
> - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> - return btd_error_invalid_args(msg);
> -
> - dbus_message_iter_get_basic(&iter, &attrstr);
> -
> - attr = attr_to_val(attrstr);
> - if (attr < 0)
> - return btd_error_not_supported(msg);
> -
> - dbus_message_iter_next(&iter);
> -
> - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
> - return btd_error_invalid_args(msg);
> -
> - dbus_message_iter_recurse(&iter, &var);
> -
> - /* Only string arguments are supported for now */
> - if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
> - return btd_error_invalid_args(msg);
> -
> - dbus_message_iter_get_basic(&var, &valstr);
> -
> - val = attrval_to_val(attr, valstr);
> - if (val < 0)
> - return btd_error_not_supported(msg);
> -
> - mp_set_attribute(mp, attr, val);
> -
> - return dbus_message_new_method_return(msg);
> -}
> -
> -static DBusMessage *mp_change_playback(DBusConnection *conn,
> - DBusMessage *msg, void *data)
> -{
> - struct audio_device *device = data;
> - struct media_player *mp = device->media_player;
> - const char *statusstr;
> - int status;
> - uint32_t elapsed;
> -
> - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr,
> - DBUS_TYPE_UINT32, &elapsed,
> - DBUS_TYPE_INVALID))
> - return btd_error_invalid_args(msg);
> -
> - status = play_status_to_val(statusstr);
> - if (status < 0)
> - return btd_error_invalid_args(msg);
> -
> - mp_set_playback_status(mp, status, elapsed);
> -
> - return dbus_message_new_method_return(msg);
> -}
> -
> -static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi)
> -{
> - DBusMessageIter dict;
> - DBusMessageIter var;
> - int ctype;
> -
> - ctype = dbus_message_iter_get_arg_type(iter);
> - if (ctype != DBUS_TYPE_ARRAY)
> - return FALSE;
> -
> - media_info_init(mi);
> - dbus_message_iter_recurse(iter, &dict);
> -
> - while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
> - DBUS_TYPE_INVALID) {
> - DBusMessageIter entry;
> - const char *key;
> -
> - if (ctype != DBUS_TYPE_DICT_ENTRY)
> - return FALSE;
> -
> - dbus_message_iter_recurse(&dict, &entry);
> - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&entry, &key);
> - dbus_message_iter_next(&entry);
> -
> - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
> - return FALSE;
> -
> - dbus_message_iter_recurse(&entry, &var);
> -
> - if (!strcmp(key, "Title")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_STRING)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->title);
> - } else if (!strcmp(key, "Artist")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_STRING)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->artist);
> - } else if (!strcmp(key, "Album")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_STRING)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->album);
> - } else if (!strcmp(key, "Genre")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_STRING)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->genre);
> - } else if (!strcmp(key, "NumberOfTracks")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_UINT32)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->ntracks);
> - } else if (!strcmp(key, "TrackNumber")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_UINT32)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->track);
> - } else if (!strcmp(key, "TrackDuration")) {
> - if (dbus_message_iter_get_arg_type(&var) !=
> - DBUS_TYPE_UINT32)
> - return FALSE;
> -
> - dbus_message_iter_get_basic(&var, &mi->track_len);
> - } else {
> - return FALSE;
> - }
> -
> - dbus_message_iter_next(&dict);
> + avctp_id = 0;
> }
> -
> - if (mi->title == NULL)
> - return FALSE;
> -
> - return TRUE;
> }
>
> -static DBusMessage *mp_change_track(DBusConnection *conn,
> - DBusMessage *msg, void *data)
> +struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
> + struct avrcp_player_cb *cb,
> + void *user_data,
> + GDestroyNotify destroy)
> {
> - struct audio_device *device = data;
> - struct media_player *mp = device->media_player;
> - DBusMessageIter iter;
> - struct media_info mi;
> -
> -
> - dbus_message_iter_init(msg, &iter);
> - if (!media_info_parse(&iter, &mi))
> - return btd_error_invalid_args(msg);
> -
> - mp_set_media_attributes(mp, &mi);
> -
> - return dbus_message_new_method_return(msg);
> -}
> -
> -static GDBusMethodTable mp_methods[] = {
> - { "SetProperty", "sv", "", mp_set_property },
> - { "ChangePlayback", "su", "", mp_change_playback },
> - { "ChangeTrack", "a{sv}", "", mp_change_track },
> - { }
> -};
> + struct avrcp_server *server;
> + struct avrcp_player *player;
>
> -static GDBusSignalTable mp_signals[] = {
> - { "PropertyChanged", "sv" },
> - { }
> -};
> + server = find_server(servers, src);
> + if (!server)
> + return NULL;
>
> -static void mp_path_unregister(void *data)
> -{
> - struct audio_device *dev = data;
> - struct media_player *mp = dev->media_player;
> + player = g_new0(struct avrcp_player, 1);
> + player->server = server;
> + player->cb = cb;
> + player->user_data = user_data;
> + player->destroy = destroy;
>
> - DBG("Unregistered interface %s on path %s",
> - MEDIA_PLAYER_INTERFACE, dev->path);
> + if (!server->players)
> + server->active_player = player;
>
> - if (mp->handler)
> - avctp_unregister_pdu_handler(mp->handler);
> + if (!avctp_id)
> + avctp_id = avctp_add_state_cb(state_changed, NULL);
>
> - g_timer_destroy(mp->timer);
> - g_free(mp);
> -}
> + server->players = g_slist_append(server->players, player);
>
> -void media_player_unregister(struct audio_device *dev)
> -{
> - g_dbus_unregister_interface(dev->conn, dev->path,
> - MEDIA_PLAYER_INTERFACE);
> + return player;
> }
>
> -struct media_player *media_player_init(struct audio_device *dev)
> +void avrcp_unregister_player(struct avrcp_player *player)
> {
> - struct media_player *mp;
> -
> - if (!g_dbus_register_interface(dev->conn, dev->path,
> - MEDIA_PLAYER_INTERFACE,
> - mp_methods, mp_signals, NULL,
> - dev, mp_path_unregister)) {
> - error("D-Bus failed do register %s on path %s",
> - MEDIA_PLAYER_INTERFACE, dev->path);
> - return NULL;
> - }
> + struct avrcp_server *server = player->server;
>
> - DBG("Registered interface %s on path %s",
> - MEDIA_PLAYER_INTERFACE, dev->path);
> + server->players = g_slist_remove(server->players, player);
>
> - mp = g_new0(struct media_player, 1);
> - mp->timer = g_timer_new();
> - mp->dev = dev;
> - media_info_init(&mp->mi);
> -
> - if (!avctp_id)
> - avctp_id = avctp_add_state_cb(state_changed, NULL);
> + if (server->active_player == player)
> + server->active_player = g_slist_nth_data(server->players, 0);
>
> - return mp;
> + player_destroy(player);
> }
> diff --git a/audio/avrcp.h b/audio/avrcp.h
> index 1fd912d..850cd6a 100644
> --- a/audio/avrcp.h
> +++ b/audio/avrcp.h
> @@ -22,7 +22,63 @@
> *
> */
>
> -#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
> +/* player attributes */
> +#define AVRCP_ATTRIBUTE_ILEGAL 0x00
> +#define AVRCP_ATTRIBUTE_EQUALIZER 0x01
> +#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02
> +#define AVRCP_ATTRIBUTE_SHUFFLE 0x03
> +#define AVRCP_ATTRIBUTE_SCAN 0x04
> +
> +/* equalizer values */
> +#define AVRCP_EQUALIZER_ON 0x01
> +#define AVRCP_EQUALIZER_OFF 0x02
> +
> +/* repeat mode values */
> +#define AVRCP_REPEAT_MODE_OFF 0x01
> +#define AVRCP_REPEAT_MODE_SINGLE 0x02
> +#define AVRCP_REPEAT_MODE_ALL 0x03
> +#define AVRCP_REPEAT_MODE_GROUP 0x04
> +
> +/* shuffle values */
> +#define AVRCP_SHUFFLE_OFF 0x01
> +#define AVRCP_SHUFFLE_ALL 0x02
> +#define AVRCP_SHUFFLE_GROUP 0x03
> +
> +/* scan values */
> +#define AVRCP_SCAN_OFF 0x01
> +#define AVRCP_SCAN_ALL 0x02
> +#define AVRCP_SCAN_GROUP 0x03
> +
> +/* media attributes */
> +#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00
> +#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01
> +#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02
> +#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03
> +#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04
> +#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05
> +#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06
> +#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07
> +#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION
> +
> +/* play status */
> +#define AVRCP_PLAY_STATUS_STOPPED 0x00
> +#define AVRCP_PLAY_STATUS_PLAYING 0x01
> +#define AVRCP_PLAY_STATUS_PAUSED 0x02
> +#define AVRCP_PLAY_STATUS_FWD_SEEK 0x03
> +#define AVRCP_PLAY_STATUS_REV_SEEK 0x04
> +#define AVRCP_PLAY_STATUS_ERROR 0xFF
> +
> +/* Notification events */
> +#define AVRCP_EVENT_STATUS_CHANGED 0x01
> +#define AVRCP_EVENT_TRACK_CHANGED 0x02
> +
> +struct avrcp_player_cb {
> + int (*get_setting) (uint8_t attr, void *user_data);
> + int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
> + void *(*get_metadata) (uint32_t id, void *user_data);
> + uint8_t (*get_status) (void *user_data);
> + uint32_t (*get_position) (void *user_data);
> +};
>
> int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
> void avrcp_unregister(const bdaddr_t *src);
> @@ -30,5 +86,10 @@ void avrcp_unregister(const bdaddr_t *src);
> gboolean avrcp_connect(struct audio_device *dev);
> void avrcp_disconnect(struct audio_device *dev);
>
> -struct media_player *media_player_init(struct audio_device *dev);
> -void media_player_unregister(struct audio_device *dev);
> +struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
> + struct avrcp_player_cb *cb,
> + void *user_data,
> + GDestroyNotify destroy);
> +void avrcp_unregister_player(struct avrcp_player *player);
> +
> +int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data);
> diff --git a/audio/device.c b/audio/device.c
> index f455bcf..a9d35f9 100644
> --- a/audio/device.c
> +++ b/audio/device.c
> @@ -731,9 +731,6 @@ void audio_device_unregister(struct audio_device *device)
> if (device->control)
> control_unregister(device);
>
> - if (device->media_player)
> - media_player_unregister(device);
> -
> g_dbus_unregister_interface(device->conn, device->path,
> AUDIO_INTERFACE);
>
> diff --git a/audio/device.h b/audio/device.h
> index 9645d93..5117fca 100644
> --- a/audio/device.h
> +++ b/audio/device.h
> @@ -44,7 +44,6 @@ struct target;
> struct sink;
> struct headset;
> struct gateway;
> -struct media_player;
> struct dev_priv;
>
> struct audio_device {
> @@ -63,7 +62,6 @@ struct audio_device {
> struct source *source;
> struct control *control;
> struct target *target;
> - struct media_player *media_player;
>
> guint hs_preauth_id;
>
> diff --git a/audio/manager.c b/audio/manager.c
> index 06d3f0e..7ec0311 100644
> --- a/audio/manager.c
> +++ b/audio/manager.c
> @@ -119,7 +119,6 @@ static struct enabled_interfaces enabled = {
> .control = TRUE,
> .socket = TRUE,
> .media = FALSE,
> - .media_player = FALSE,
> };
>
> static struct audio_adapter *find_adapter(GSList *list,
> @@ -224,8 +223,6 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
> else
> device->control = control_init(device, uuid16);
>
> - if (enabled.media_player && !device->media_player)
> - device->media_player = media_player_init(device);
> if (device->sink && sink_is_active(device))
> avrcp_connect(device);
> break;
> @@ -1177,8 +1174,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
> enabled.socket = TRUE;
> else if (g_str_equal(list[i], "Media"))
> enabled.media = TRUE;
> - else if (g_str_equal(list[i], "MediaPlayer"))
> - enabled.media_player = TRUE;
>
> }
> g_strfreev(list);
> @@ -1200,8 +1195,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
> enabled.socket = FALSE;
> else if (g_str_equal(list[i], "Media"))
> enabled.media = FALSE;
> - else if (g_str_equal(list[i], "MediaPlayer"))
> - enabled.media_player = FALSE;
> }
> g_strfreev(list);
>
> diff --git a/audio/media.c b/audio/media.c
> index 5ab3eab..593b5dd 100644
> --- a/audio/media.c
> +++ b/audio/media.c
> @@ -42,6 +42,7 @@
> #include "media.h"
> #include "transport.h"
> #include "a2dp.h"
> +#include "avrcp.h"
> #include "headset.h"
> #include "gateway.h"
> #include "manager.h"
> @@ -52,6 +53,7 @@
>
> #define MEDIA_INTERFACE "org.bluez.Media"
> #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
> +#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
>
> #define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
>
> @@ -60,6 +62,7 @@ struct media_adapter {
> char *path; /* Adapter path */
> DBusConnection *conn; /* Adapter connection */
> GSList *endpoints; /* Endpoints list */
> + GSList *players; /* Players list */
> };
>
> struct endpoint_request {
> @@ -86,6 +89,29 @@ struct media_endpoint {
> struct media_adapter *adapter;
> };
>
> +struct media_player {
> + struct media_adapter *adapter;
> + struct avrcp_player *player;
> + char *sender; /* Player DBus bus id */
> + char *path; /* Player object path */
> + GHashTable *settings; /* Player settings */
> + GHashTable *track; /* Player current track */
> + guint watch;
> + guint property_watch;
> + guint track_watch;
> + uint8_t status;
> + uint32_t position;
> + GTimer *timer;
> +};
> +
> +struct metadata_value {
> + int type;
> + union {
> + char *str;
> + uint32_t num;
> + } value;
> +};
> +
> static GSList *adapters = NULL;
>
> static void endpoint_request_free(struct endpoint_request *request)
> @@ -822,9 +848,759 @@ static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
> return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> }
>
> +static struct media_player *media_adapter_find_player(
> + struct media_adapter *adapter,
> + const char *sender,
> + const char *path)
> +{
> + GSList *l;
> +
> + for (l = adapter->endpoints; l; l = l->next) {
> + struct media_player *mp = l->data;
> +
> + if (sender && g_strcmp0(mp->sender, sender) != 0)
> + continue;
> +
> + if (path && g_strcmp0(mp->path, path) != 0)
> + continue;
> +
> + return mp;
> + }
> +
> + return NULL;
> +}
> +
> +static void media_player_free(gpointer data)
> +{
> + struct media_player *mp = data;
> + struct media_adapter *adapter = mp->adapter;
> +
> + g_dbus_remove_watch(adapter->conn, mp->watch);
> + g_dbus_remove_watch(adapter->conn, mp->property_watch);
> + g_dbus_remove_watch(adapter->conn, mp->track_watch);
> +
> + if (mp->track)
> + g_hash_table_unref(mp->track);
> +
> + if (mp->settings)
> + g_hash_table_unref(mp->settings);
> +
> + g_free(mp->sender);
> + g_free(mp->path);
> + g_free(mp);
> +}
> +
> +static void media_player_destroy(struct media_player *mp)
> +{
> + DBG("sender=%s path=%s", mp->sender, mp->path);
> +
> + if (mp->player) {
> + avrcp_unregister_player(mp->player);
> + return;
> + }
> +
> + media_player_free(mp);
> +}
> +
> +static void media_player_remove(struct media_player *mp)
> +{
> + info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
> +
> + media_player_destroy(mp);
> +}
> +
> +static const char *attrval_to_str(uint8_t attr, uint8_t value)
> +{
> + switch (attr) {
> + case AVRCP_ATTRIBUTE_EQUALIZER:
> + switch (value) {
> + case AVRCP_EQUALIZER_ON:
> + return "on";
> + case AVRCP_EQUALIZER_OFF:
> + return "off";
> + }
> +
> + break;
> + case AVRCP_ATTRIBUTE_REPEAT_MODE:
> + switch (value) {
> + case AVRCP_REPEAT_MODE_OFF:
> + return "off";
> + case AVRCP_REPEAT_MODE_SINGLE:
> + return "singletrack";
> + case AVRCP_REPEAT_MODE_ALL:
> + return "alltracks";
> + case AVRCP_REPEAT_MODE_GROUP:
> + return "group";
> + }
> +
> + break;
> + /* Shuffle and scan have the same values */
> + case AVRCP_ATTRIBUTE_SHUFFLE:
> + case AVRCP_ATTRIBUTE_SCAN:
> + switch (value) {
> + case AVRCP_SCAN_OFF:
> + return "off";
> + case AVRCP_SCAN_ALL:
> + return "alltracks";
> + case AVRCP_SCAN_GROUP:
> + return "group";
> + }
> +
> + break;
> + }
> +
> + return NULL;
> +}
> +
> +static int attrval_to_val(uint8_t attr, const char *value)
> +{
> + int ret;
> +
> + switch (attr) {
> + case AVRCP_ATTRIBUTE_EQUALIZER:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_EQUALIZER_OFF;
> + else if (!strcmp(value, "on"))
> + ret = AVRCP_EQUALIZER_ON;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_REPEAT_MODE:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_REPEAT_MODE_OFF;
> + else if (!strcmp(value, "singletrack"))
> + ret = AVRCP_REPEAT_MODE_SINGLE;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_REPEAT_MODE_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_REPEAT_MODE_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_SHUFFLE:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_SHUFFLE_OFF;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_SHUFFLE_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_SHUFFLE_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_SCAN:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_SCAN_OFF;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_SCAN_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_SCAN_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const char *attr_to_str(uint8_t attr)
> +{
> + switch (attr) {
> + case AVRCP_ATTRIBUTE_EQUALIZER:
> + return "Equalizer";
> + case AVRCP_ATTRIBUTE_REPEAT_MODE:
> + return "Repeat";
> + case AVRCP_ATTRIBUTE_SHUFFLE:
> + return "Shuffle";
> + case AVRCP_ATTRIBUTE_SCAN:
> + return "Scan";
> + }
> +
> + return NULL;
> +}
> +
> +static int attr_to_val(const char *str)
> +{
> + if (!strcasecmp(str, "Equalizer"))
> + return AVRCP_ATTRIBUTE_EQUALIZER;
> + else if (!strcasecmp(str, "Repeat"))
> + return AVRCP_ATTRIBUTE_REPEAT_MODE;
> + else if (!strcasecmp(str, "Shuffle"))
> + return AVRCP_ATTRIBUTE_SHUFFLE;
> + else if (!strcasecmp(str, "Scan"))
> + return AVRCP_ATTRIBUTE_SCAN;
> +
> + return -EINVAL;
> +}
> +
> +static int play_status_to_val(const char *status)
> +{
> + if (!strcasecmp(status, "stopped"))
> + return AVRCP_PLAY_STATUS_STOPPED;
> + else if (!strcasecmp(status, "playing"))
> + return AVRCP_PLAY_STATUS_PLAYING;
> + else if (!strcasecmp(status, "paused"))
> + return AVRCP_PLAY_STATUS_PAUSED;
> + else if (!strcasecmp(status, "forward-seek"))
> + return AVRCP_PLAY_STATUS_FWD_SEEK;
> + else if (!strcasecmp(status, "reverse-seek"))
> + return AVRCP_PLAY_STATUS_REV_SEEK;
> + else if (!strcasecmp(status, "error"))
> + return AVRCP_PLAY_STATUS_ERROR;
> +
> + return -EINVAL;
> +}
> +
> +static int metadata_to_val(const char *str)
> +{
> + if (!strcasecmp(str, "Title"))
> + return AVRCP_MEDIA_ATTRIBUTE_TITLE;
> + else if (!strcasecmp(str, "Artist"))
> + return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
> + else if (!strcasecmp(str, "Album"))
> + return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
> + else if (!strcasecmp(str, "Genre"))
> + return AVRCP_MEDIA_ATTRIBUTE_GENRE;
> + else if (!strcasecmp(str, "NumberOfTracks"))
> + return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
> + else if (!strcasecmp(str, "Number"))
> + return AVRCP_MEDIA_ATTRIBUTE_TRACK;
> + else if (!strcasecmp(str, "Duration"))
> + return AVRCP_MEDIA_ATTRIBUTE_DURATION;
> +
> + return -EINVAL;
> +}
> +
> +static const char *metadata_to_str(uint32_t id)
> +{
> + switch (id) {
> + case AVRCP_MEDIA_ATTRIBUTE_TITLE:
> + return "Title";
> + case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
> + return "Artist";
> + case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
> + return "Album";
> + case AVRCP_MEDIA_ATTRIBUTE_GENRE:
> + return "Genre";
> + case AVRCP_MEDIA_ATTRIBUTE_TRACK:
> + return "Track";
> + case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
> + return "NumberOfTracks";
> + case AVRCP_MEDIA_ATTRIBUTE_DURATION:
> + return "Duration";
> + }
> +
> + return NULL;
> +}
> +
> +static int get_setting(uint8_t attr, void *user_data)
> +{
> + struct media_player *mp = user_data;
> + void *value;
> +
> + DBG("%s", attr_to_str(attr));
> +
> + value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> + if (!value)
> + return -EINVAL;
> +
> + return GPOINTER_TO_UINT(value);
> +}
> +
> +static int set_setting(uint8_t attr, uint8_t val, void *user_data)
> +{
> + struct media_player *mp = user_data;
> + struct media_adapter *adapter = mp->adapter;
> + const char *property, *value;
> + DBusMessage *msg;
> + DBusMessageIter iter, var;
> +
> + property = attr_to_str(attr);
> + value = attrval_to_str(attr, val);
> +
> + DBG("%s = %s", property, value);
> +
> + if (property == NULL || value == NULL)
> + return -EINVAL;
> +
> + msg = dbus_message_new_method_call(mp->sender, mp->path,
> + MEDIA_PLAYER_INTERFACE,
> + "SetProperty");
> + if (msg == NULL) {
> + error("Couldn't allocate D-Bus message");
> + return -ENOMEM;
> + }
> +
> + dbus_message_iter_init_append(msg, &iter);
> + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
> +
> + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
> + DBUS_TYPE_STRING_AS_STRING,
> + &var);
> + dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &value);
> + dbus_message_iter_close_container(&iter, &var);
> +
> + g_dbus_send_message(adapter->conn, msg);
> +
> + return 0;
> +}
> +
> +static void *get_metadata(uint32_t id, void *user_data)
> +{
> + struct media_player *mp = user_data;
> + struct metadata_value *value;
> +
> + DBG("%s", metadata_to_str(id));
> +
> + value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
> + if (!value)
> + return NULL;
> +
> + switch (value->type) {
> + case DBUS_TYPE_STRING:
> + return value->value.str;
> + case DBUS_TYPE_UINT32:
> + return GUINT_TO_POINTER(value->value.num);
> + }
> +
> + return NULL;
> +}
> +
> +static uint8_t get_status(void *user_data)
> +{
> + struct media_player *mp = user_data;
> +
> + return mp->status;
> +}
> +
> +static uint32_t get_position(void *user_data)
> +{
> + struct media_player *mp = user_data;
> + double timedelta;
> + uint32_t sec, msec;
> +
> + if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
> + return mp->position;
> +
> + timedelta = g_timer_elapsed(mp->timer, NULL);
> +
> + sec = (uint32_t) timedelta;
> + msec = (uint32_t) ((timedelta - sec) * 1000);
> +
> + return mp->position + sec * 1000 + msec;

Looks right now.

> +}
> +
> +static struct avrcp_player_cb player_cb = {
> + .get_setting = get_setting,
> + .set_setting = set_setting,
> + .get_metadata = get_metadata,
> + .get_position = get_position,
> + .get_status = get_status
> +};
> +
> +static void media_player_exit(DBusConnection *connection, void *user_data)
> +{
> + struct media_player *mp = user_data;
> +
> + mp->watch = 0;
> + media_player_remove(mp);
> +}
> +
> +static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
> +{
> + const char *value;
> + int val;
> +
> + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
> + return FALSE;
> +
> + dbus_message_iter_get_basic(iter, &value);
> + DBG("Status=%s", value);
> +
> + val = play_status_to_val(value);
> + if (val < 0) {
> + error("Invalid status");
> + return FALSE;
> + }
> +
> + if (mp->status == val)
> + return TRUE;
> +

There's a problem here. A solution would be to make the following calls:

mp->position = get_position(mp);
g_timer_start(mp->timer);


> + mp->status = val;
> +
> + avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
> +
> + return TRUE;
> +}
> +
> +static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
> +{
> + uint32_t value;
> +
> + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
> + return FALSE;
> +
> + dbus_message_iter_get_basic(iter, &value);
> + DBG("Position=%u", value);
> +
> + mp->position = value;
> + g_timer_start(mp->timer);
> +
> + return TRUE;
> +}
> +
> +static gboolean set_property(struct media_player *mp, const char *key,
> + DBusMessageIter *entry)
> +{
> + DBusMessageIter var;
> + const char *value;
> + int attr, val;
> +
> + if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
> + return FALSE;
> +
> + dbus_message_iter_recurse(entry, &var);
> +
> + if (strcasecmp(key, "Status") == 0)
> + return set_status(mp, &var);
> +
> + if (strcasecmp(key, "Position") == 0)
> + return set_position(mp, &var);
> +
> + attr = attr_to_val(key);
> + if (attr < 0)
> + return FALSE;
> +
> + if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
> + return FALSE;
> +
> + dbus_message_iter_get_basic(&var, &value);
> +
> + val = attrval_to_val(attr, value);
> + if (val < 0)
> + return FALSE;
> +
> + DBG("%s=%s", key, value);
> +
> + if (!mp->settings)
> + mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
> +
> + g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
> + GUINT_TO_POINTER(val));
> +
> + return TRUE;
> +}
> +
> +static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
> + void *user_data)
> +{
> + struct media_player *mp = user_data;
> + DBusMessageIter iter, entry;
> + const char *property;
> +
> + DBG("sender=%s path=%s", mp->sender, mp->path);
> +
> + dbus_message_iter_init(msg, &iter);
> +
> + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
> + error("Unexpected signature in %s.%s signal",
> + dbus_message_get_interface(msg),
> + dbus_message_get_member(msg));
> + return TRUE;
> + }
> +
> + dbus_message_iter_get_basic(&iter, &property);
> +
> + dbus_message_iter_next(&iter);
> + dbus_message_iter_recurse(&iter, &entry);
> +
> + set_property(mp, property, &entry);
> +
> + return TRUE;
> +}
> +
> +static void metadata_value_free(gpointer data)
> +{
> + struct metadata_value *value = data;
> +
> + switch (value->type) {
> + case DBUS_TYPE_STRING:
> + g_free(value->value.str);
> + break;
> + }
> +
> + g_free(value);
> +}
> +
> +static gboolean parse_player_metadata(struct media_player *mp,
> + DBusMessageIter *iter)
> +{
> + DBusMessageIter dict;
> + DBusMessageIter var;
> + int ctype;
> + gboolean title = FALSE;
> +
> + ctype = dbus_message_iter_get_arg_type(iter);
> + if (ctype != DBUS_TYPE_ARRAY)
> + return FALSE;
> +
> + dbus_message_iter_recurse(iter, &dict);
> +
> + while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
> + DBUS_TYPE_INVALID) {
> + DBusMessageIter entry;
> + const char *key;
> + struct metadata_value *value;
> + int id;
> +
> + if (ctype != DBUS_TYPE_DICT_ENTRY)
> + return FALSE;
> +
> + dbus_message_iter_recurse(&dict, &entry);
> + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
> + return FALSE;
> +
> + dbus_message_iter_get_basic(&entry, &key);
> + dbus_message_iter_next(&entry);
> +
> + id = metadata_to_val(key);
> + if (id < 0)
> + return FALSE;
> +
> + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
> + return FALSE;
> +
> + dbus_message_iter_recurse(&entry, &var);
> +
> + value = g_new0(struct metadata_value, 1);
> + value->type = dbus_message_iter_get_arg_type(&var);
> +
> + switch (id) {
> + case AVRCP_MEDIA_ATTRIBUTE_TITLE:
> + title = TRUE;
> + case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
> + case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
> + case AVRCP_MEDIA_ATTRIBUTE_GENRE:
> + if (value->type != DBUS_TYPE_STRING) {
> + g_free(value);
> + return FALSE;
> + }
> +
> + dbus_message_iter_get_basic(&var, &value->value.str);
> + break;
> + case AVRCP_MEDIA_ATTRIBUTE_TRACK:
> + case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
> + case AVRCP_MEDIA_ATTRIBUTE_DURATION:
> + if (value->type != DBUS_TYPE_UINT32) {
> + g_free(value);
> + return FALSE;
> + }
> +
> + dbus_message_iter_get_basic(&var, &value->value.num);
> + break;
> + default:
> + return FALSE;
> + }
> +
> + switch (value->type) {
> + case DBUS_TYPE_STRING:
> + value->value.str = g_strdup(value->value.str);
> + DBG("%s=%s", key, value->value.str);
> + break;
> + default:
> + DBG("%s=%u", key, value->value.num);
> + }
> +
> + if (!mp->track)
> + mp->track = g_hash_table_new_full(g_direct_hash,
> + g_direct_equal, NULL,
> + metadata_value_free);
> +
> + g_hash_table_replace(mp->track, GUINT_TO_POINTER(id), value);
> + dbus_message_iter_next(&dict);
> + }
> +
> + mp->position = 0;
> + g_timer_start(mp->timer);

Is there any reason not to move these lines to track_changed()? See
comment below.

> +
> + return title;
> +}
> +
> +static gboolean track_changed(DBusConnection *connection, DBusMessage *msg,
> + void *user_data)
> +{
> + struct media_player *mp = user_data;
> + DBusMessageIter iter;
> +
> + DBG("sender=%s path=%s", mp->sender, mp->path);
> +
> + dbus_message_iter_init(msg, &iter);
> +
> + if (parse_player_metadata(mp, &iter) == FALSE) {
> + error("Unexpected signature in %s.%s signal",
> + dbus_message_get_interface(msg),
> + dbus_message_get_member(msg));

If there's no title, you will still set position to 0 and reseting
the timer.

If app sends a signal track_changed({ "Title": "Bla",
"Artist": "Bli",
"IdontKnowThisName", "Blu",
"Track": 2 })

then you would partially set the fields of the track, log an error and
consider it was started. Not a nice thing to have. This is why I was
setting the fields on stack and if the msg was successfully
parsed I copied it over to mp.

> + }
> +
> + avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
> +
> + return TRUE;
> +}
> +
> +static struct media_player *media_player_create(struct media_adapter *adapter,
> + const char *sender,
> + const char *path,
> + int *err)
> +{
> + struct media_player *mp;
> +
> + mp = g_new0(struct media_player, 1);
> + mp->adapter = adapter;
> + mp->sender = g_strdup(sender);
> + mp->path = g_strdup(path);
> + mp->timer = g_timer_new();
> +
> + mp->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
> + media_player_exit, mp,
> + NULL);
> + mp->property_watch = g_dbus_add_signal_watch(adapter->conn, sender,
> + path, MEDIA_PLAYER_INTERFACE,
> + "PropertyChanged",
> + property_changed,
> + mp, NULL);
> + mp->track_watch = g_dbus_add_signal_watch(adapter->conn, sender,
> + path, MEDIA_PLAYER_INTERFACE,
> + "TrackChanged",
> + track_changed,
> + mp, NULL);
> + mp->player = avrcp_register_player(&adapter->src, &player_cb, mp,
> + media_player_free);
> + if (!mp->player) {
> + if (err)
> + *err = -EPROTONOSUPPORT;
> + media_player_destroy(mp);
> + return NULL;
> + }
> +
> + adapter->players = g_slist_append(adapter->players, mp);
> +
> + info("Player registered: sender=%s path=%s", sender, path);
> +
> + if (err)
> + *err = 0;
> +
> + return mp;
> +}
> +
> +static gboolean parse_player_properties(struct media_player *mp,
> + DBusMessageIter *iter)
> +{
> + DBusMessageIter dict;
> + int ctype;
> +
> + ctype = dbus_message_iter_get_arg_type(iter);
> + if (ctype != DBUS_TYPE_ARRAY)
> + return FALSE;
> +
> + dbus_message_iter_recurse(iter, &dict);
> +
> + while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
> + DBUS_TYPE_INVALID) {
> + DBusMessageIter entry;
> + const char *key;
> +
> + if (ctype != DBUS_TYPE_DICT_ENTRY)
> + return FALSE;
> +
> + dbus_message_iter_recurse(&dict, &entry);
> + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
> + return FALSE;
> +
> + dbus_message_iter_get_basic(&entry, &key);
> + dbus_message_iter_next(&entry);
> +
> + if (set_property(mp, key, &entry) == FALSE)
> + return FALSE;
> +
> + dbus_message_iter_next(&dict);
> + }
> +
> + return TRUE;
> +}
> +
> +static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg,
> + void *data)
> +{
> + struct media_adapter *adapter = data;
> + struct media_player *mp;
> + DBusMessageIter args;
> + const char *sender, *path;
> + int err;
> +
> + sender = dbus_message_get_sender(msg);
> +
> + dbus_message_iter_init(msg, &args);
> +
> + dbus_message_iter_get_basic(&args, &path);
> + dbus_message_iter_next(&args);
> +
> + if (media_adapter_find_player(adapter, sender, path) != NULL)
> + return btd_error_already_exists(msg);
> +
> + mp = media_player_create(adapter, sender, path, &err);
> + if (mp == NULL) {
> + if (err == -EPROTONOSUPPORT)
> + return btd_error_not_supported(msg);
> + else
> + return btd_error_invalid_args(msg);
> + }
> +
> + if (parse_player_properties(mp, &args) == FALSE) {
> + media_player_destroy(mp);
> + return btd_error_invalid_args(msg);
> + }
> +
> + dbus_message_iter_next(&args);
> +
> + if (parse_player_metadata(mp, &args) == FALSE) {
> + media_player_destroy(mp);
> + return btd_error_invalid_args(msg);
> + }
> +
> + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> +}
> +
> +static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg,
> + void *data)
> +{
> + struct media_adapter *adapter = data;
> + struct media_player *player;
> + const char *sender, *path;
> +
> + if (!dbus_message_get_args(msg, NULL,
> + DBUS_TYPE_OBJECT_PATH, &path,
> + DBUS_TYPE_INVALID))
> + return NULL;
> +
> + sender = dbus_message_get_sender(msg);
> +
> + player = media_adapter_find_player(adapter, sender, path);
> + if (player == NULL)
> + return btd_error_does_not_exist(msg);
> +
> + media_player_remove(player);
> +
> + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> +}
> +
> static GDBusMethodTable media_methods[] = {
> { "RegisterEndpoint", "oa{sv}", "", register_endpoint },
> { "UnregisterEndpoint", "o", "", unregister_endpoint },
> + { "RegisterPlayer", "oa{sv}a{sv}","", register_player },
> + { "UnregisterPlayer", "o", "", unregister_player },
> { },
> };
>
> diff --git a/doc/media-api.txt b/doc/media-api.txt
> index b2f239a..af4cfa0 100644
> --- a/doc/media-api.txt
> +++ b/doc/media-api.txt
> @@ -44,6 +44,169 @@ Methods void RegisterEndpoint(object endpoint, dict properties)
>
> Unregister sender end point.
>
> + void RegisterPlayer(object player, dict properties,
> + dict metadata)
> +
> + Register a media player object to sender, the sender
> + can register as many objets as it likes.
> +
> + Note: If the sender disconnects its objects are
> + automatically unregistered.
> +
> + Properties:
> +
> + string Equalizer:
> +
> + Possible values: "off" or "on"
> +
> + string Repeat:
> +
> + Possible values: "off", "singletrack",
> + "alltracks" or "group"
> +
> + string Shuffle:
> +
> + Possible values: "off", "alltracks" or
> + "group"
> +
> + string Scan:
> +
> + Possible values: "off", "alltracks" or
> + "group"
> +
> + string Status:
> +
> + Possible values: "playing", "stopped",
> + "paused",
> + "forward-seek",
> + "reverse-seek" or
> + "error"
> +
> + uint32 Position
> +
> + Playback position in milliseconds
> +
> + Metadata:
> +
> + string Title:
> +
> + Track title name
> +
> + string Artist:
> +
> + Track artist name
> +
> + string Album:
> +
> + Track album name
> +
> + string Genre:
> +
> + Track genre name
> +
> + uint32 NumberOfTracks:
> +
> + Number of tracks in total
> +
> + uint32 Number:
> +
> + Track number
> +
> + uint32 Duration:
> +
> + Track duration in milliseconds
> +
> + Possible Errors: org.bluez.Error.InvalidArguments
> + org.bluez.Error.NotSupported
> +
> + void UnregisterPlayer(object player)
> +
> + Unregister sender media player.
> +
> +MediaPlayer hierarchy
> +=====================
> +
> +Service unique name
> +Interface org.bluez.MediaPlayer
> +Object path freely definable
> +
> +Methods void SetProperty(string property, variant value)
> +
> + Changes the value of the specified property. Only
> + properties that are listed a read-write can be changed.
> +
> + On success this will emit a PropertyChanged signal.
> +
> +Signals PropertyChanged(string setting, variant value)
> +
> + This signal indicates a changed value of the given
> + property.
> +
> + TrackChanged(dict metadata)
> +
> + Possible values:
> +
> + string Title:
> +
> + Track title name
> +
> + string Artist:
> +
> + Track artist name
> +
> + string Album:
> +
> + Track album name
> +
> + string Genre:
> +
> + Track genre name
> +
> + uint32 NumberOfTracks:
> +
> + Number of tracks in total
> +
> + uint32 Number:
> +
> + Track number
> +
> + uint32 Duration:
> +
> + Track duration in milliseconds
> +
> + StatusChanged(string status, uint32 position)
> +
> + Possible status: "playing", "stopped", "paused",
> + "forward-seek", "reverse-seek" or
> + "error"
> +
> +Properties string Equalizer [readwrite]
> +
> + Possible values: "off" or "on"
> +
> + string Repeat [readwrite]
> +
> + Possible values: "off", "singletrack", "alltracks" or
> + "group"
> +
> + string Shuffle [readwrite]
> +
> + Possible values: "off", "alltracks" or "group"
> +
> + string Scan [readwrite]
> +
> + Possible values: "off", "alltracks" or "group"
> +
> + string Status [readonly]
> +
> + Possible status: "playing", "stopped", "paused",
> + "forward-seek", "reverse-seek" or
> + "error"
> +
> + uint32 Position [readonly]
> +
> + Playback position in milliseconds
> +
> MediaEndpoint hierarchy
> =======================
>
> --
> 1.7.6.4


Otherwise it seems pretty good. Nice work.

How are the tests going? I have some fixes queued here. I'll send them
on top of your patches so you don't waste your time at UPF debugging
them.


Thanks
Lucas De Marchi

2011-10-03 22:01:16

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 3/5] Add simple-player test script

From: Luiz Augusto von Dentz <[email protected]>

---
test/simple-player | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)
create mode 100755 test/simple-player

diff --git a/test/simple-player b/test/simple-player
new file mode 100755
index 0000000..f483897
--- /dev/null
+++ b/test/simple-player
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+class Player(dbus.service.Object):
+ @dbus.service.method("org.bluez.MediaPlayer",
+ in_signature="sv", out_signature="")
+ def SetProperty(self, key, value):
+ print "SetProperty (%s, %s)" % (key, value)
+ return
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+ else:
+ path = manager.DefaultAdapter()
+
+ media = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Media")
+
+ path = "/test/player"
+ player = Player(bus, path)
+ mainloop = gobject.MainLoop()
+
+ properties = dbus.Dictionary({ "Equalizer" : "off",
+ "Repeat" : "off",
+ "Shuffle" : "off",
+ "Scan" : "off",
+ "Status" : "playing",
+ "Position" : dbus.UInt32(0) })
+
+ print properties
+
+ metadata = dbus.Dictionary({ "Title" : "Title",
+ "Artist" : "Artist",
+ "Album" : "Album",
+ "Genre" : "Genre",
+ "NumberOfTracks" : dbus.UInt32(10),
+ "Number" : dbus.UInt32(1),
+ "Duration" : dbus.UInt32(10000) })
+
+ print metadata
+
+ media.RegisterPlayer(path, properties, metadata)
+
+ mainloop.run()
--
1.7.6.4


2011-10-03 22:01:15

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 2/5] AVRCP: move MediaPlayer to adapter object

From: Luiz Augusto von Dentz <[email protected]>

This move the MediaPlayer registration to adapter object on Media
interface so we can track players properly.
---
audio/avrcp.c | 832 +++++++++++------------------------------------------
audio/avrcp.h | 67 ++++-
audio/device.c | 3 -
audio/device.h | 2 -
audio/manager.c | 7 -
audio/media.c | 776 +++++++++++++++++++++++++++++++++++++++++++++++++
doc/media-api.txt | 163 +++++++++++
7 files changed, 1167 insertions(+), 683 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 6e8b8ff..cfdb313 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -78,54 +78,10 @@
#define AVRCP_GET_PLAY_STATUS 0x30
#define AVRCP_REGISTER_NOTIFICATION 0x31

-/* Notification events */
-#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED 0x01
-#define AVRCP_EVENT_TRACK_CHANGED 0x02
-
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
#define CAP_EVENTS_SUPPORTED 0x03

-enum player_setting {
- PLAYER_SETTING_EQUALIZER = 1,
- PLAYER_SETTING_REPEAT = 2,
- PLAYER_SETTING_SHUFFLE = 3,
- PLAYER_SETTING_SCAN = 4,
-};
-
-enum equalizer_mode {
- EQUALIZER_MODE_OFF = 1,
- EQUALIZER_MODE_ON = 2,
-};
-
-enum repeat_mode {
- REPEAT_MODE_OFF = 1,
- REPEAT_MODE_SINGLE = 2,
- REPEAT_MODE_ALL = 3,
- REPEAT_MODE_GROUP = 4,
-};
-
-enum shuffle_mode {
- SHUFFLE_MODE_OFF = 1,
- SHUFFLE_MODE_ALL = 2,
- SHUFFLE_MODE_GROUP = 3,
-};
-
-enum scan_mode {
- SCAN_MODE_OFF = 1,
- SCAN_MODE_ALL = 2,
- SCAN_MODE_GROUP = 3,
-};
-
-enum play_status {
- PLAY_STATUS_STOPPED = 0x00,
- PLAY_STATUS_PLAYING = 0x01,
- PLAY_STATUS_PAUSED = 0x02,
- PLAY_STATUS_FWD_SEEK = 0x03,
- PLAY_STATUS_REV_SEEK = 0x04,
- PLAY_STATUS_ERROR = 0xFF
-};
-
enum battery_status {
BATTERY_STATUS_NORMAL = 0,
BATTERY_STATUS_WARNING = 1,
@@ -134,17 +90,6 @@ enum battery_status {
BATTERY_STATUS_FULL_CHARGE = 4,
};

-enum media_info_id {
- MEDIA_INFO_TITLE = 1,
- MEDIA_INFO_ARTIST = 2,
- MEDIA_INFO_ALBUM = 3,
- MEDIA_INFO_TRACK = 4,
- MEDIA_INFO_N_TRACKS = 5,
- MEDIA_INFO_GENRE = 6,
- MEDIA_INFO_PLAYING_TIME = 7,
- MEDIA_INFO_LAST
-};
-
#if __BYTE_ORDER == __LITTLE_ENDIAN

struct avrcp_header {
@@ -180,33 +125,26 @@ struct avrcp_server {
bdaddr_t src;
uint32_t tg_record_id;
uint32_t ct_record_id;
+ GSList *players;
+ struct avrcp_player *active_player;
};

-struct media_info {
- char *title;
- char *artist;
- char *album;
- char *genre;
- uint32_t ntracks;
- uint32_t track;
- uint32_t track_len;
- uint32_t elapsed;
-};
-
-struct media_player {
+struct avrcp_player {
+ struct avrcp_server *server;
struct avctp *session;
struct audio_device *dev;
- uint8_t settings[PLAYER_SETTING_SCAN + 1];
- enum play_status status;

- struct media_info mi;
- GTimer *timer;
unsigned int handler;
uint16_t registered_events;
uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
+
+ struct avrcp_player_cb *cb;
+ void *user_data;
+ GDestroyNotify destroy;
};

static GSList *servers = NULL;
+static unsigned int avctp_id = 0;

/* Company IDs supported by this device */
static uint32_t company_ids[] = {
@@ -344,164 +282,19 @@ static sdp_record_t *avrcp_tg_record(void)
static unsigned int attr_get_max_val(uint8_t attr)
{
switch (attr) {
- case PLAYER_SETTING_EQUALIZER:
- return EQUALIZER_MODE_ON;
- case PLAYER_SETTING_REPEAT:
- return REPEAT_MODE_GROUP;
- case PLAYER_SETTING_SHUFFLE:
- return SHUFFLE_MODE_GROUP;
- case PLAYER_SETTING_SCAN:
- return SCAN_MODE_GROUP;
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return AVRCP_EQUALIZER_ON;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return AVRCP_REPEAT_MODE_GROUP;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return AVRCP_SHUFFLE_GROUP;
+ case AVRCP_ATTRIBUTE_SCAN:
+ return AVRCP_SCAN_GROUP;
}

return 0;
}

-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
- switch (attr) {
- case PLAYER_SETTING_EQUALIZER:
- switch (value) {
- case EQUALIZER_MODE_ON:
- return "on";
- case EQUALIZER_MODE_OFF:
- return "off";
- }
-
- break;
- case PLAYER_SETTING_REPEAT:
- switch (value) {
- case REPEAT_MODE_OFF:
- return "off";
- case REPEAT_MODE_SINGLE:
- return "singletrack";
- case REPEAT_MODE_ALL:
- return "alltracks";
- case REPEAT_MODE_GROUP:
- return "group";
- }
-
- break;
- /* Shuffle and scan have the same values */
- case PLAYER_SETTING_SHUFFLE:
- case PLAYER_SETTING_SCAN:
- switch (value) {
- case SCAN_MODE_OFF:
- return "off";
- case SCAN_MODE_ALL:
- return "alltracks";
- case SCAN_MODE_GROUP:
- return "group";
- }
-
- break;
- }
-
- return NULL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
- int ret;
-
- switch (attr) {
- case PLAYER_SETTING_EQUALIZER:
- if (!strcmp(value, "off"))
- ret = EQUALIZER_MODE_OFF;
- else if (!strcmp(value, "on"))
- ret = EQUALIZER_MODE_ON;
- else
- ret = -EINVAL;
-
- return ret;
- case PLAYER_SETTING_REPEAT:
- if (!strcmp(value, "off"))
- ret = REPEAT_MODE_OFF;
- else if (!strcmp(value, "singletrack"))
- ret = REPEAT_MODE_SINGLE;
- else if (!strcmp(value, "alltracks"))
- ret = REPEAT_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = REPEAT_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case PLAYER_SETTING_SHUFFLE:
- if (!strcmp(value, "off"))
- ret = SHUFFLE_MODE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = SHUFFLE_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = SHUFFLE_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case PLAYER_SETTING_SCAN:
- if (!strcmp(value, "off"))
- ret = SCAN_MODE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = SCAN_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = SCAN_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- }
-
- return -EINVAL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
- switch (attr) {
- case PLAYER_SETTING_EQUALIZER:
- return "Equalizer";
- case PLAYER_SETTING_REPEAT:
- return "Repeat";
- case PLAYER_SETTING_SHUFFLE:
- return "Shuffle";
- case PLAYER_SETTING_SCAN:
- return "Scan";
- }
-
- return NULL;
-}
-
-static int attr_to_val(const char *str)
-{
- if (!strcmp(str, "Equalizer"))
- return PLAYER_SETTING_EQUALIZER;
- else if (!strcmp(str, "Repeat"))
- return PLAYER_SETTING_REPEAT;
- else if (!strcmp(str, "Shuffle"))
- return PLAYER_SETTING_SHUFFLE;
- else if (!strcmp(str, "Scan"))
- return PLAYER_SETTING_SCAN;
-
- return -EINVAL;
-}
-
-static int play_status_to_val(const char *status)
-{
- if (!strcmp(status, "stopped"))
- return PLAY_STATUS_STOPPED;
- else if (!strcmp(status, "playing"))
- return PLAY_STATUS_PLAYING;
- else if (!strcmp(status, "paused"))
- return PLAY_STATUS_PAUSED;
- else if (!strcmp(status, "forward-seek"))
- return PLAY_STATUS_FWD_SEEK;
- else if (!strcmp(status, "reverse-seek"))
- return PLAY_STATUS_REV_SEEK;
- else if (!strcmp(status, "error"))
- return PLAY_STATUS_ERROR;
-
- return -EINVAL;
-}
-
static const char *battery_status_to_str(enum battery_status status)
{
switch (status) {
@@ -542,17 +335,17 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}

-static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
+int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
uint16_t size;
int err;

- if (mp->session == NULL)
+ if (player->session == NULL)
return -ENOTCONN;

- if (!(mp->registered_events & (1 << id)))
+ if (!(player->registered_events & (1 << id)))
return 0;

memset(buf, 0, sizeof(buf));
@@ -565,7 +358,7 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
DBG("id=%u", id);

switch (id) {
- case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+ case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
pdu->params[1] = *((uint8_t *)data);

@@ -589,58 +382,18 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)

pdu->params_len = htons(size);

- err = avctp_send_vendordep(mp->session, mp->transaction_events[id],
+ err = avctp_send_vendordep(player->session, player->transaction_events[id],
AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
if (err < 0)
return err;

/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- mp->registered_events ^= 1 << id;
+ player->registered_events ^= 1 << id;

return 0;
}

-static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
- uint32_t *elapsed, uint32_t *track_len)
-{
- if (status)
- *status = mp->status;
- if (track_len)
- *track_len = mp->mi.track_len;
-
- if (!elapsed)
- return;
-
- *elapsed = mp->mi.elapsed;
-
- if (mp->status == PLAY_STATUS_PLAYING) {
- double timedelta = g_timer_elapsed(mp->timer, NULL);
- uint32_t sec, msec;
-
- sec = (uint32_t) timedelta;
- msec = (uint32_t)((timedelta - sec) * 1000);
-
- *elapsed += sec * 1000 + msec;
- }
-}
-
-static void mp_set_playback_status(struct media_player *mp, uint8_t status,
- uint32_t elapsed)
-{
- DBG("Change playback: %u %u", status, elapsed);
-
- mp->mi.elapsed = elapsed;
- g_timer_start(mp->timer);
-
- if (status == mp->status)
- return;
-
- mp->status = status;
-
- avrcp_send_event(mp, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED, &status);
-}
-
/*
* Copy media_info field to a buffer, intended to be used in a response to
* GetElementAttributes message.
@@ -648,10 +401,10 @@ static void mp_set_playback_status(struct media_player *mp, uint8_t status,
* It assumes there's enough space in the buffer and on success it returns the
* size written.
*
- * If @param id is not valid, -EINVAL is returned. If there's no such media
- * attribute, -ENOENT is returned.
+ * If @param id is not valid, -EINVAL is returned. If there's no space left on
+ * the buffer -ENOBUFS is returned.
*/
-static int mp_get_media_attribute(struct media_player *mp,
+static int player_get_media_attribute(struct avrcp_player *player,
uint32_t id, uint8_t *buf,
uint16_t maxlen)
{
@@ -661,11 +414,10 @@ static int mp_get_media_attribute(struct media_player *mp,
uint16_t len;
uint8_t val[];
};
- const struct media_info *mi = &mp->mi;
struct media_info_elem *elem = (void *)buf;
uint16_t len;
char valstr[20];
- char *valp;
+ void *value;

if (maxlen < sizeof(struct media_info_elem))
return -ENOBUFS;
@@ -673,61 +425,38 @@ static int mp_get_media_attribute(struct media_player *mp,
/* Subtract the size of elem header from the available space */
maxlen -= sizeof(struct media_info_elem);

- switch (id) {
- case MEDIA_INFO_TITLE:
- valp = mi->title;
- break;
- case MEDIA_INFO_ARTIST:
- valp = mi->artist;
- break;
- case MEDIA_INFO_ALBUM:
- valp = mi->album;
- break;
- case MEDIA_INFO_GENRE:
- valp = mi->genre;
- break;
- case MEDIA_INFO_TRACK:
- if (mi->track) {
- snprintf(valstr, 20, "%u", mi->track);
- valp = valstr;
- } else {
- valp = NULL;
- }
+ DBG("Get media attribute: %u", id);

- break;
- case MEDIA_INFO_N_TRACKS:
- if (mi->ntracks) {
- snprintf(valstr, 20, "%u", mi->ntracks);
- valp = valstr;
- } else {
- valp = NULL;
- }
+ value = player->cb->get_metadata(id, player->user_data);
+ if (value == NULL) {
+ len = 0;
+ goto done;
+ }

+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ len = strlen((char *) value);
+ if (len > maxlen)
+ return -ENOBUFS;
+ memcpy(elem->val, value, len);
break;
- case MEDIA_INFO_PLAYING_TIME:
- if (mi->track_len == 0xFFFFFFFF) {
- snprintf(valstr, 20, "%u", mi->track_len);
- valp = valstr;
- } else {
- valp = NULL;
- }
-
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value));
+ len = strlen(valstr);
+ if (len > maxlen)
+ return -ENOBUFS;
+ memcpy(elem->val, valstr, len);
break;
default:
return -ENOENT;
}

- if (valp) {
- len = strlen(valp);
-
- if (len > maxlen)
- return -ENOBUFS;
-
- memcpy(elem->val, valp, len);
- } else {
- len = 0;
- }
-
+done:
elem->id = htonl(id);
elem->charset = htons(0x6A); /* Always use UTF-8 */
elem->len = htons(len);
@@ -735,57 +464,22 @@ static int mp_get_media_attribute(struct media_player *mp,
return sizeof(struct media_info_elem) + len;
}

-static void mp_set_attribute(struct media_player *mp,
+static int player_set_attribute(struct avrcp_player *player,
uint8_t attr, uint8_t val)
{
DBG("Change attribute: %u %u", attr, val);

- mp->settings[attr] = val;
+ return player->cb->set_setting(attr, val, player->user_data);
}

-static int mp_get_attribute(struct media_player *mp, uint8_t attr)
+static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
{
DBG("Get attribute: %u", attr);

- return mp->settings[attr];
-}
-
-static void mp_set_media_attributes(struct media_player *mp,
- struct media_info *mi)
-{
- g_free(mp->mi.title);
- mp->mi.title = g_strdup(mi->title);
-
- g_free(mp->mi.artist);
- mp->mi.artist = g_strdup(mi->artist);
-
- g_free(mp->mi.album);
- mp->mi.album = g_strdup(mi->album);
-
- g_free(mp->mi.genre);
- mp->mi.genre = g_strdup(mi->genre);
-
- mp->mi.ntracks = mi->ntracks;
- mp->mi.track = mi->track;
- mp->mi.track_len = mi->track_len;
-
- /*
- * elapsed is special. Whenever the track changes, we reset it to 0,
- * so client doesn't have to make another call to change_playback
- */
- mp->mi.elapsed = 0;
- g_timer_start(mp->timer);
-
- DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
- "\tgenre: %s\n\tNumber of tracks: %u\n"
- "\tTrack number: %u\n\tTrack duration: %u",
- mi->title, mi->artist, mi->album, mi->genre,
- mi->ntracks, mi->track, mi->track_len);
-
- avrcp_send_event(mp, AVRCP_EVENT_TRACK_CHANGED, NULL);
+ return player->cb->get_setting(attr, player->user_data);
}

-static uint8_t avrcp_handle_get_capabilities(struct media_player *mp,
+static uint8_t avrcp_handle_get_capabilities(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -811,7 +505,7 @@ static uint8_t avrcp_handle_get_capabilities(struct media_player *mp,
case CAP_EVENTS_SUPPORTED:
pdu->params_len = htons(4);
pdu->params[1] = 2;
- pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
+ pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;

return AVC_CTYPE_STABLE;
@@ -824,7 +518,7 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp,
+static uint8_t avrcp_handle_list_player_attributes(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -837,11 +531,11 @@ static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp,
return AVC_CTYPE_REJECTED;
}

- if (!mp)
+ if (!player)
goto done;

- for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
- if (!mp_get_attribute(mp, i)) {
+ for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
+ if (!player_get_attribute(player, i)) {
DBG("Ignoring setting %u: not supported by player", i);
continue;
}
@@ -857,14 +551,14 @@ done:
return AVC_CTYPE_STABLE;
}

-static uint8_t avrcp_handle_list_player_values(struct media_player *mp,
+static uint8_t avrcp_handle_list_player_values(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
unsigned int i;

- if (len != 1 || !mp)
+ if (len != 1 || !player)
goto err;

len = attr_get_max_val(pdu->params[0]);
@@ -887,7 +581,7 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
+static uint8_t avrcp_handle_get_element_attributes(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -910,8 +604,9 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
* Return all available information, at least
* title must be returned.
*/
- for (i = 1; i < MEDIA_INFO_LAST; i++) {
- size = mp_get_media_attribute(mp, i, &pdu->params[pos],
+ for (i = 1; i < AVRCP_MEDIA_ATTRIBUTE_LAST; i++) {
+ size = player_get_media_attribute(player, i,
+ &pdu->params[pos],
AVRCP_PDU_MTU - pos);

if (size > 0) {
@@ -927,7 +622,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
for (i = 0; i < nattr; i++) {
uint32_t attr = ntohl(attr_ids[i]);

- size = mp_get_media_attribute(mp, attr,
+ size = player_get_media_attribute(player, attr,
&pdu->params[pos],
AVRCP_PDU_MTU - pos);

@@ -953,7 +648,7 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
+static uint8_t avrcp_handle_get_current_player_value(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -961,7 +656,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
uint8_t *settings;
unsigned int i;

- if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
+ if (player == NULL || len <= 1 || pdu->params[0] != len - 1)
goto err;

/*
@@ -979,13 +674,13 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
for (i = 0; i < pdu->params[0]; i++) {
uint8_t val;

- if (settings[i] < PLAYER_SETTING_EQUALIZER ||
- settings[i] > PLAYER_SETTING_SCAN) {
+ if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER ||
+ settings[i] > AVRCP_ATTRIBUTE_SCAN) {
DBG("Ignoring %u", settings[i]);
continue;
}

- val = mp_get_attribute(mp, settings[i]);
+ val = player_get_attribute(player, settings[i]);
if (!val) {
DBG("Ignoring %u: not supported by player",
settings[i]);
@@ -1014,7 +709,7 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
+static uint8_t avrcp_handle_set_player_value(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -1036,23 +731,11 @@ static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
for (i = 1; i < pdu->params[0]; i += 2) {
uint8_t attr = pdu->params[i];
uint8_t val = pdu->params[i + 1];
- const char *attrstr;
- const char *valstr;
-
- attrstr = attr_to_str(attr);
- if (!attrstr)
- continue;

- valstr = attrval_to_str(attr, val);
- if (!valstr)
+ if (player_set_attribute(player, attr, val) < 0)
continue;

len++;
-
- mp_set_attribute(mp, attr, val);
- emit_property_changed(mp->dev->conn, mp->dev->path,
- MEDIA_PLAYER_INTERFACE, attrstr,
- DBUS_TYPE_STRING, &valstr);
}

if (len) {
@@ -1067,7 +750,7 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_displayable_charset(struct media_player *mp,
+static uint8_t avrcp_handle_displayable_charset(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -1087,7 +770,7 @@ static uint8_t avrcp_handle_displayable_charset(struct media_player *mp,
return AVC_CTYPE_STABLE;
}

-static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp,
+static uint8_t avrcp_handle_ct_battery_status(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
@@ -1101,9 +784,6 @@ static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp,
if (valstr == NULL)
goto err;

- emit_property_changed(mp->dev->conn, mp->dev->path,
- MEDIA_PLAYER_INTERFACE, "Battery",
- DBUS_TYPE_STRING, &valstr);
pdu->params_len = 0;

return AVC_CTYPE_STABLE;
@@ -1114,14 +794,13 @@ err:
return AVC_CTYPE_REJECTED;
}

-static uint8_t avrcp_handle_get_play_status(struct media_player *mp,
+static uint8_t avrcp_handle_get_play_status(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
- uint32_t elapsed;
- uint32_t track_len;
- uint8_t status;
+ uint32_t position;
+ uint32_t duration;

if (len != 0) {
pdu->params_len = htons(1);
@@ -1129,25 +808,28 @@ static uint8_t avrcp_handle_get_play_status(struct media_player *mp,
return AVC_CTYPE_REJECTED;
}

- mp_get_playback_status(mp, &status, &elapsed, &track_len);
- track_len = htonl(track_len);
- elapsed = htonl(elapsed);
+ position = player->cb->get_position(player->user_data);
+ duration = GPOINTER_TO_UINT(player->cb->get_metadata(
+ AVRCP_MEDIA_ATTRIBUTE_DURATION,
+ player->user_data));
+
+ duration = htonl(duration);
+ position = htonl(position);

- memcpy(&pdu->params[0], &track_len, 4);
- memcpy(&pdu->params[4], &elapsed, 4);
- pdu->params[8] = status;
+ memcpy(&pdu->params[0], &duration, 4);
+ memcpy(&pdu->params[4], &position, 4);
+ pdu->params[8] = player->cb->get_status(player->user_data);;

pdu->params_len = htons(9);

return AVC_CTYPE_STABLE;
}

-static uint8_t avrcp_handle_register_notification(struct media_player *mp,
+static uint8_t avrcp_handle_register_notification(struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
- uint8_t status;

/*
* 1 byte for EventID, 4 bytes for Playback interval but the latest
@@ -1158,10 +840,9 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp,
goto err;

switch (pdu->params[0]) {
- case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+ case AVRCP_EVENT_STATUS_CHANGED:
len = 2;
- mp_get_playback_status(mp, &status, NULL, NULL);
- pdu->params[1] = status;
+ pdu->params[1] = player->cb->get_status(player->user_data);

break;
case AVRCP_EVENT_TRACK_CHANGED:
@@ -1176,8 +857,8 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp,
}

/* Register event and save the transaction used */
- mp->registered_events |= (1 << pdu->params[0]);
- mp->transaction_events[pdu->params[0]] = transaction;
+ player->registered_events |= (1 << pdu->params[0]);
+ player->transaction_events[pdu->params[0]] = transaction;

pdu->params_len = htons(len);

@@ -1192,7 +873,7 @@ err:
static struct pdu_handler {
uint8_t pdu_id;
uint8_t code;
- uint8_t (*func) (struct media_player *mp,
+ uint8_t (*func) (struct avrcp_player *player,
struct avrcp_header *pdu,
uint8_t transaction);
} handlers[] = {
@@ -1229,7 +910,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
- struct media_player *mp = user_data;
+ struct avrcp_player *player = user_data;
struct pdu_handler *handler;
struct avrcp_header *pdu = (void *) operands;
uint32_t company_id = get_company_id(pdu->company_id);
@@ -1265,7 +946,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
goto err_metadata;
}

- *code = handler->func(mp, pdu, transaction);
+ *code = handler->func(player, pdu, transaction);

return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);

@@ -1276,52 +957,56 @@ err_metadata:
return AVRCP_HEADER_LENGTH + 1;
}

+static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+ for (; list; list = list->next) {
+ struct avrcp_server *server = list->data;
+
+ if (bacmp(&server->src, src) == 0)
+ return server;
+ }
+
+ return NULL;
+}
+
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
avctp_state_t new_state, void *user_data)
{
- struct media_player *mp = dev->media_player;
+ struct avrcp_server *server;
+ struct avrcp_player *player;

+ server = find_server(servers, &dev->src);
+ if (!server)
+ return;

- if (!mp)
+ player = server->active_player;
+ if (!player)
return;

switch (new_state) {
case AVCTP_STATE_DISCONNECTED:
- mp->session = NULL;
+ player->session = NULL;

- if (mp->handler) {
- avctp_unregister_pdu_handler(mp->handler);
- mp->handler = 0;
+ if (player->handler) {
+ avctp_unregister_pdu_handler(player->handler);
+ player->handler = 0;
}

break;
case AVCTP_STATE_CONNECTING:
- mp->session = avctp_connect(&dev->src, &dev->dst);
+ player->session = avctp_connect(&dev->src, &dev->dst);

- if (!mp->handler)
- mp->handler = avctp_register_pdu_handler(
+ if (!player->handler)
+ player->handler = avctp_register_pdu_handler(
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
- mp);
+ player);
break;
default:
return;
}
}

-static void media_info_init(struct media_info *mi)
-{
- memset(mi, 0, sizeof(*mi));
-
- /*
- * As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG
- * does not support these attributes (i.e. they were never set via
- * D-Bus)
- */
- mi->track_len = 0xFFFFFFFF;
- mi->elapsed = 0xFFFFFFFF;
-}
-
gboolean avrcp_connect(struct audio_device *dev)
{
struct avctp *session;
@@ -1344,8 +1029,6 @@ void avrcp_disconnect(struct audio_device *dev)
avctp_disconnect(session);
}

-static unsigned int avctp_id = 0;
-
int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
{
sdp_record_t *record;
@@ -1390,7 +1073,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
}

if (add_record_to_server(src, record) < 0) {
- error("Unable to register AVRCP mpler service record");
+ error("Unable to register AVRCP service record");
sdp_record_free(record);
g_free(server);
return -1;
@@ -1411,16 +1094,17 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
return 0;
}

-static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
+static void player_destroy(gpointer data)
{
- for (; list; list = list->next) {
- struct avrcp_server *server = list->data;
+ struct avrcp_player *player = data;

- if (bacmp(&server->src, src) == 0)
- return server;
- }
+ if (player->destroy)
+ player->destroy(player->user_data);

- return NULL;
+ if (player->handler)
+ avctp_unregister_pdu_handler(player->handler);
+
+ g_free(player);
}

void avrcp_unregister(const bdaddr_t *src)
@@ -1431,6 +1115,8 @@ void avrcp_unregister(const bdaddr_t *src)
if (!server)
return;

+ g_slist_free_full(server->players, player_destroy);
+
servers = g_slist_remove(servers, server);

remove_record_from_server(server->ct_record_id);
@@ -1442,239 +1128,49 @@ void avrcp_unregister(const bdaddr_t *src)
if (servers)
return;

- if (avctp_id)
+ if (avctp_id) {
avctp_remove_state_cb(avctp_id);
-}
-
-static DBusMessage *mp_set_property(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct audio_device *device = data;
- struct media_player *mp = device->media_player;
- DBusMessageIter iter;
- DBusMessageIter var;
- const char *attrstr, *valstr;
- int attr, val;
-
- if (!dbus_message_iter_init(msg, &iter))
- return btd_error_invalid_args(msg);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&iter, &attrstr);
-
- attr = attr_to_val(attrstr);
- if (attr < 0)
- return btd_error_not_supported(msg);
-
- dbus_message_iter_next(&iter);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_recurse(&iter, &var);
-
- /* Only string arguments are supported for now */
- if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&var, &valstr);
-
- val = attrval_to_val(attr, valstr);
- if (val < 0)
- return btd_error_not_supported(msg);
-
- mp_set_attribute(mp, attr, val);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *mp_change_playback(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct audio_device *device = data;
- struct media_player *mp = device->media_player;
- const char *statusstr;
- int status;
- uint32_t elapsed;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr,
- DBUS_TYPE_UINT32, &elapsed,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- status = play_status_to_val(statusstr);
- if (status < 0)
- return btd_error_invalid_args(msg);
-
- mp_set_playback_status(mp, status, elapsed);
-
- return dbus_message_new_method_return(msg);
-}
-
-static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi)
-{
- DBusMessageIter dict;
- DBusMessageIter var;
- int ctype;
-
- ctype = dbus_message_iter_get_arg_type(iter);
- if (ctype != DBUS_TYPE_ARRAY)
- return FALSE;
-
- media_info_init(mi);
- dbus_message_iter_recurse(iter, &dict);
-
- while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
- DBUS_TYPE_INVALID) {
- DBusMessageIter entry;
- const char *key;
-
- if (ctype != DBUS_TYPE_DICT_ENTRY)
- return FALSE;
-
- dbus_message_iter_recurse(&dict, &entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(&entry, &key);
- dbus_message_iter_next(&entry);
-
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
- return FALSE;
-
- dbus_message_iter_recurse(&entry, &var);
-
- if (!strcmp(key, "Title")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->title);
- } else if (!strcmp(key, "Artist")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->artist);
- } else if (!strcmp(key, "Album")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->album);
- } else if (!strcmp(key, "Genre")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->genre);
- } else if (!strcmp(key, "NumberOfTracks")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_UINT32)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->ntracks);
- } else if (!strcmp(key, "TrackNumber")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_UINT32)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->track);
- } else if (!strcmp(key, "TrackDuration")) {
- if (dbus_message_iter_get_arg_type(&var) !=
- DBUS_TYPE_UINT32)
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &mi->track_len);
- } else {
- return FALSE;
- }
-
- dbus_message_iter_next(&dict);
+ avctp_id = 0;
}
-
- if (mi->title == NULL)
- return FALSE;
-
- return TRUE;
}

-static DBusMessage *mp_change_track(DBusConnection *conn,
- DBusMessage *msg, void *data)
+struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
+ struct avrcp_player_cb *cb,
+ void *user_data,
+ GDestroyNotify destroy)
{
- struct audio_device *device = data;
- struct media_player *mp = device->media_player;
- DBusMessageIter iter;
- struct media_info mi;
-
-
- dbus_message_iter_init(msg, &iter);
- if (!media_info_parse(&iter, &mi))
- return btd_error_invalid_args(msg);
-
- mp_set_media_attributes(mp, &mi);
-
- return dbus_message_new_method_return(msg);
-}
-
-static GDBusMethodTable mp_methods[] = {
- { "SetProperty", "sv", "", mp_set_property },
- { "ChangePlayback", "su", "", mp_change_playback },
- { "ChangeTrack", "a{sv}", "", mp_change_track },
- { }
-};
+ struct avrcp_server *server;
+ struct avrcp_player *player;

-static GDBusSignalTable mp_signals[] = {
- { "PropertyChanged", "sv" },
- { }
-};
+ server = find_server(servers, src);
+ if (!server)
+ return NULL;

-static void mp_path_unregister(void *data)
-{
- struct audio_device *dev = data;
- struct media_player *mp = dev->media_player;
+ player = g_new0(struct avrcp_player, 1);
+ player->server = server;
+ player->cb = cb;
+ player->user_data = user_data;
+ player->destroy = destroy;

- DBG("Unregistered interface %s on path %s",
- MEDIA_PLAYER_INTERFACE, dev->path);
+ if (!server->players)
+ server->active_player = player;

- if (mp->handler)
- avctp_unregister_pdu_handler(mp->handler);
+ if (!avctp_id)
+ avctp_id = avctp_add_state_cb(state_changed, NULL);

- g_timer_destroy(mp->timer);
- g_free(mp);
-}
+ server->players = g_slist_append(server->players, player);

-void media_player_unregister(struct audio_device *dev)
-{
- g_dbus_unregister_interface(dev->conn, dev->path,
- MEDIA_PLAYER_INTERFACE);
+ return player;
}

-struct media_player *media_player_init(struct audio_device *dev)
+void avrcp_unregister_player(struct avrcp_player *player)
{
- struct media_player *mp;
-
- if (!g_dbus_register_interface(dev->conn, dev->path,
- MEDIA_PLAYER_INTERFACE,
- mp_methods, mp_signals, NULL,
- dev, mp_path_unregister)) {
- error("D-Bus failed do register %s on path %s",
- MEDIA_PLAYER_INTERFACE, dev->path);
- return NULL;
- }
+ struct avrcp_server *server = player->server;

- DBG("Registered interface %s on path %s",
- MEDIA_PLAYER_INTERFACE, dev->path);
+ server->players = g_slist_remove(server->players, player);

- mp = g_new0(struct media_player, 1);
- mp->timer = g_timer_new();
- mp->dev = dev;
- media_info_init(&mp->mi);
-
- if (!avctp_id)
- avctp_id = avctp_add_state_cb(state_changed, NULL);
+ if (server->active_player == player)
+ server->active_player = g_slist_nth_data(server->players, 0);

- return mp;
+ player_destroy(player);
}
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 1fd912d..850cd6a 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -22,7 +22,63 @@
*
*/

-#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+/* player attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL 0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER 0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE 0x03
+#define AVRCP_ATTRIBUTE_SCAN 0x04
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_ON 0x01
+#define AVRCP_EQUALIZER_OFF 0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF 0x01
+#define AVRCP_REPEAT_MODE_SINGLE 0x02
+#define AVRCP_REPEAT_MODE_ALL 0x03
+#define AVRCP_REPEAT_MODE_GROUP 0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF 0x01
+#define AVRCP_SHUFFLE_ALL 0x02
+#define AVRCP_SHUFFLE_GROUP 0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF 0x01
+#define AVRCP_SCAN_ALL 0x02
+#define AVRCP_SCAN_GROUP 0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* play status */
+#define AVRCP_PLAY_STATUS_STOPPED 0x00
+#define AVRCP_PLAY_STATUS_PLAYING 0x01
+#define AVRCP_PLAY_STATUS_PAUSED 0x02
+#define AVRCP_PLAY_STATUS_FWD_SEEK 0x03
+#define AVRCP_PLAY_STATUS_REV_SEEK 0x04
+#define AVRCP_PLAY_STATUS_ERROR 0xFF
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED 0x01
+#define AVRCP_EVENT_TRACK_CHANGED 0x02
+
+struct avrcp_player_cb {
+ int (*get_setting) (uint8_t attr, void *user_data);
+ int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+ void *(*get_metadata) (uint32_t id, void *user_data);
+ uint8_t (*get_status) (void *user_data);
+ uint32_t (*get_position) (void *user_data);
+};

int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
void avrcp_unregister(const bdaddr_t *src);
@@ -30,5 +86,10 @@ void avrcp_unregister(const bdaddr_t *src);
gboolean avrcp_connect(struct audio_device *dev);
void avrcp_disconnect(struct audio_device *dev);

-struct media_player *media_player_init(struct audio_device *dev);
-void media_player_unregister(struct audio_device *dev);
+struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
+ struct avrcp_player_cb *cb,
+ void *user_data,
+ GDestroyNotify destroy);
+void avrcp_unregister_player(struct avrcp_player *player);
+
+int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data);
diff --git a/audio/device.c b/audio/device.c
index f455bcf..a9d35f9 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -731,9 +731,6 @@ void audio_device_unregister(struct audio_device *device)
if (device->control)
control_unregister(device);

- if (device->media_player)
- media_player_unregister(device);
-
g_dbus_unregister_interface(device->conn, device->path,
AUDIO_INTERFACE);

diff --git a/audio/device.h b/audio/device.h
index 9645d93..5117fca 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -44,7 +44,6 @@ struct target;
struct sink;
struct headset;
struct gateway;
-struct media_player;
struct dev_priv;

struct audio_device {
@@ -63,7 +62,6 @@ struct audio_device {
struct source *source;
struct control *control;
struct target *target;
- struct media_player *media_player;

guint hs_preauth_id;

diff --git a/audio/manager.c b/audio/manager.c
index 06d3f0e..7ec0311 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -119,7 +119,6 @@ static struct enabled_interfaces enabled = {
.control = TRUE,
.socket = TRUE,
.media = FALSE,
- .media_player = FALSE,
};

static struct audio_adapter *find_adapter(GSList *list,
@@ -224,8 +223,6 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
else
device->control = control_init(device, uuid16);

- if (enabled.media_player && !device->media_player)
- device->media_player = media_player_init(device);
if (device->sink && sink_is_active(device))
avrcp_connect(device);
break;
@@ -1177,8 +1174,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.socket = TRUE;
else if (g_str_equal(list[i], "Media"))
enabled.media = TRUE;
- else if (g_str_equal(list[i], "MediaPlayer"))
- enabled.media_player = TRUE;

}
g_strfreev(list);
@@ -1200,8 +1195,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.socket = FALSE;
else if (g_str_equal(list[i], "Media"))
enabled.media = FALSE;
- else if (g_str_equal(list[i], "MediaPlayer"))
- enabled.media_player = FALSE;
}
g_strfreev(list);

diff --git a/audio/media.c b/audio/media.c
index 5ab3eab..593b5dd 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -42,6 +42,7 @@
#include "media.h"
#include "transport.h"
#include "a2dp.h"
+#include "avrcp.h"
#include "headset.h"
#include "gateway.h"
#include "manager.h"
@@ -52,6 +53,7 @@

#define MEDIA_INTERFACE "org.bluez.Media"
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"

#define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */

@@ -60,6 +62,7 @@ struct media_adapter {
char *path; /* Adapter path */
DBusConnection *conn; /* Adapter connection */
GSList *endpoints; /* Endpoints list */
+ GSList *players; /* Players list */
};

struct endpoint_request {
@@ -86,6 +89,29 @@ struct media_endpoint {
struct media_adapter *adapter;
};

+struct media_player {
+ struct media_adapter *adapter;
+ struct avrcp_player *player;
+ char *sender; /* Player DBus bus id */
+ char *path; /* Player object path */
+ GHashTable *settings; /* Player settings */
+ GHashTable *track; /* Player current track */
+ guint watch;
+ guint property_watch;
+ guint track_watch;
+ uint8_t status;
+ uint32_t position;
+ GTimer *timer;
+};
+
+struct metadata_value {
+ int type;
+ union {
+ char *str;
+ uint32_t num;
+ } value;
+};
+
static GSList *adapters = NULL;

static void endpoint_request_free(struct endpoint_request *request)
@@ -822,9 +848,759 @@ static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}

+static struct media_player *media_adapter_find_player(
+ struct media_adapter *adapter,
+ const char *sender,
+ const char *path)
+{
+ GSList *l;
+
+ for (l = adapter->endpoints; l; l = l->next) {
+ struct media_player *mp = l->data;
+
+ if (sender && g_strcmp0(mp->sender, sender) != 0)
+ continue;
+
+ if (path && g_strcmp0(mp->path, path) != 0)
+ continue;
+
+ return mp;
+ }
+
+ return NULL;
+}
+
+static void media_player_free(gpointer data)
+{
+ struct media_player *mp = data;
+ struct media_adapter *adapter = mp->adapter;
+
+ g_dbus_remove_watch(adapter->conn, mp->watch);
+ g_dbus_remove_watch(adapter->conn, mp->property_watch);
+ g_dbus_remove_watch(adapter->conn, mp->track_watch);
+
+ if (mp->track)
+ g_hash_table_unref(mp->track);
+
+ if (mp->settings)
+ g_hash_table_unref(mp->settings);
+
+ g_free(mp->sender);
+ g_free(mp->path);
+ g_free(mp);
+}
+
+static void media_player_destroy(struct media_player *mp)
+{
+ DBG("sender=%s path=%s", mp->sender, mp->path);
+
+ if (mp->player) {
+ avrcp_unregister_player(mp->player);
+ return;
+ }
+
+ media_player_free(mp);
+}
+
+static void media_player_remove(struct media_player *mp)
+{
+ info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
+
+ media_player_destroy(mp);
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ switch (value) {
+ case AVRCP_EQUALIZER_ON:
+ return "on";
+ case AVRCP_EQUALIZER_OFF:
+ return "off";
+ }
+
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ switch (value) {
+ case AVRCP_REPEAT_MODE_OFF:
+ return "off";
+ case AVRCP_REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case AVRCP_REPEAT_MODE_ALL:
+ return "alltracks";
+ case AVRCP_REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ case AVRCP_ATTRIBUTE_SCAN:
+ switch (value) {
+ case AVRCP_SCAN_OFF:
+ return "off";
+ case AVRCP_SCAN_ALL:
+ return "alltracks";
+ case AVRCP_SCAN_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+ int ret;
+
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_EQUALIZER_OFF;
+ else if (!strcmp(value, "on"))
+ ret = AVRCP_EQUALIZER_ON;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_REPEAT_MODE_OFF;
+ else if (!strcmp(value, "singletrack"))
+ ret = AVRCP_REPEAT_MODE_SINGLE;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_REPEAT_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_REPEAT_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SHUFFLE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SHUFFLE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SHUFFLE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SCAN:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SCAN_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SCAN_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SCAN_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return "Equalizer";
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return "Repeat";
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return "Shuffle";
+ case AVRCP_ATTRIBUTE_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
+static int attr_to_val(const char *str)
+{
+ if (!strcasecmp(str, "Equalizer"))
+ return AVRCP_ATTRIBUTE_EQUALIZER;
+ else if (!strcasecmp(str, "Repeat"))
+ return AVRCP_ATTRIBUTE_REPEAT_MODE;
+ else if (!strcasecmp(str, "Shuffle"))
+ return AVRCP_ATTRIBUTE_SHUFFLE;
+ else if (!strcasecmp(str, "Scan"))
+ return AVRCP_ATTRIBUTE_SCAN;
+
+ return -EINVAL;
+}
+
+static int play_status_to_val(const char *status)
+{
+ if (!strcasecmp(status, "stopped"))
+ return AVRCP_PLAY_STATUS_STOPPED;
+ else if (!strcasecmp(status, "playing"))
+ return AVRCP_PLAY_STATUS_PLAYING;
+ else if (!strcasecmp(status, "paused"))
+ return AVRCP_PLAY_STATUS_PAUSED;
+ else if (!strcasecmp(status, "forward-seek"))
+ return AVRCP_PLAY_STATUS_FWD_SEEK;
+ else if (!strcasecmp(status, "reverse-seek"))
+ return AVRCP_PLAY_STATUS_REV_SEEK;
+ else if (!strcasecmp(status, "error"))
+ return AVRCP_PLAY_STATUS_ERROR;
+
+ return -EINVAL;
+}
+
+static int metadata_to_val(const char *str)
+{
+ if (!strcasecmp(str, "Title"))
+ return AVRCP_MEDIA_ATTRIBUTE_TITLE;
+ else if (!strcasecmp(str, "Artist"))
+ return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
+ else if (!strcasecmp(str, "Album"))
+ return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
+ else if (!strcasecmp(str, "Genre"))
+ return AVRCP_MEDIA_ATTRIBUTE_GENRE;
+ else if (!strcasecmp(str, "NumberOfTracks"))
+ return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
+ else if (!strcasecmp(str, "Number"))
+ return AVRCP_MEDIA_ATTRIBUTE_TRACK;
+ else if (!strcasecmp(str, "Duration"))
+ return AVRCP_MEDIA_ATTRIBUTE_DURATION;
+
+ return -EINVAL;
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ return "Title";
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ return "Artist";
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ return "Album";
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ return "Genre";
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ return "Track";
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ return "NumberOfTracks";
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ return "Duration";
+ }
+
+ return NULL;
+}
+
+static int get_setting(uint8_t attr, void *user_data)
+{
+ struct media_player *mp = user_data;
+ void *value;
+
+ DBG("%s", attr_to_str(attr));
+
+ value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ if (!value)
+ return -EINVAL;
+
+ return GPOINTER_TO_UINT(value);
+}
+
+static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+{
+ struct media_player *mp = user_data;
+ struct media_adapter *adapter = mp->adapter;
+ const char *property, *value;
+ DBusMessage *msg;
+ DBusMessageIter iter, var;
+
+ property = attr_to_str(attr);
+ value = attrval_to_str(attr, val);
+
+ DBG("%s = %s", property, value);
+
+ if (property == NULL || value == NULL)
+ return -EINVAL;
+
+ msg = dbus_message_new_method_call(mp->sender, mp->path,
+ MEDIA_PLAYER_INTERFACE,
+ "SetProperty");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING,
+ &var);
+ dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&iter, &var);
+
+ g_dbus_send_message(adapter->conn, msg);
+
+ return 0;
+}
+
+static void *get_metadata(uint32_t id, void *user_data)
+{
+ struct media_player *mp = user_data;
+ struct metadata_value *value;
+
+ DBG("%s", metadata_to_str(id));
+
+ value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+ if (!value)
+ return NULL;
+
+ switch (value->type) {
+ case DBUS_TYPE_STRING:
+ return value->value.str;
+ case DBUS_TYPE_UINT32:
+ return GUINT_TO_POINTER(value->value.num);
+ }
+
+ return NULL;
+}
+
+static uint8_t get_status(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ return mp->status;
+}
+
+static uint32_t get_position(void *user_data)
+{
+ struct media_player *mp = user_data;
+ double timedelta;
+ uint32_t sec, msec;
+
+ if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->timer, NULL);
+
+ sec = (uint32_t) timedelta;
+ msec = (uint32_t) ((timedelta - sec) * 1000);
+
+ return mp->position + sec * 1000 + msec;
+}
+
+static struct avrcp_player_cb player_cb = {
+ .get_setting = get_setting,
+ .set_setting = set_setting,
+ .get_metadata = get_metadata,
+ .get_position = get_position,
+ .get_status = get_status
+};
+
+static void media_player_exit(DBusConnection *connection, void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ mp->watch = 0;
+ media_player_remove(mp);
+}
+
+static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
+{
+ const char *value;
+ int val;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+ DBG("Status=%s", value);
+
+ val = play_status_to_val(value);
+ if (val < 0) {
+ error("Invalid status");
+ return FALSE;
+ }
+
+ if (mp->status == val)
+ return TRUE;
+
+ mp->status = val;
+
+ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+
+ return TRUE;
+}
+
+static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
+{
+ uint32_t value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+ DBG("Position=%u", value);
+
+ mp->position = value;
+ g_timer_start(mp->timer);
+
+ return TRUE;
+}
+
+static gboolean set_property(struct media_player *mp, const char *key,
+ DBusMessageIter *entry)
+{
+ DBusMessageIter var;
+ const char *value;
+ int attr, val;
+
+ if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+ return FALSE;
+
+ dbus_message_iter_recurse(entry, &var);
+
+ if (strcasecmp(key, "Status") == 0)
+ return set_status(mp, &var);
+
+ if (strcasecmp(key, "Position") == 0)
+ return set_position(mp, &var);
+
+ attr = attr_to_val(key);
+ if (attr < 0)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ val = attrval_to_val(attr, value);
+ if (val < 0)
+ return FALSE;
+
+ DBG("%s=%s", key, value);
+
+ if (!mp->settings)
+ mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
+ GUINT_TO_POINTER(val));
+
+ return TRUE;
+}
+
+static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
+ void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBusMessageIter iter, entry;
+ const char *property;
+
+ DBG("sender=%s path=%s", mp->sender, mp->path);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &entry);
+
+ set_property(mp, property, &entry);
+
+ return TRUE;
+}
+
+static void metadata_value_free(gpointer data)
+{
+ struct metadata_value *value = data;
+
+ switch (value->type) {
+ case DBUS_TYPE_STRING:
+ g_free(value->value.str);
+ break;
+ }
+
+ g_free(value);
+}
+
+static gboolean parse_player_metadata(struct media_player *mp,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+ DBusMessageIter var;
+ int ctype;
+ gboolean title = FALSE;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+ struct metadata_value *value;
+ int id;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ id = metadata_to_val(key);
+ if (id < 0)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return FALSE;
+
+ dbus_message_iter_recurse(&entry, &var);
+
+ value = g_new0(struct metadata_value, 1);
+ value->type = dbus_message_iter_get_arg_type(&var);
+
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ title = TRUE;
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ break;
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ break;
+ default:
+ return FALSE;
+ }
+
+ switch (value->type) {
+ case DBUS_TYPE_STRING:
+ value->value.str = g_strdup(value->value.str);
+ DBG("%s=%s", key, value->value.str);
+ break;
+ default:
+ DBG("%s=%u", key, value->value.num);
+ }
+
+ if (!mp->track)
+ mp->track = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL,
+ metadata_value_free);
+
+ g_hash_table_replace(mp->track, GUINT_TO_POINTER(id), value);
+ dbus_message_iter_next(&dict);
+ }
+
+ mp->position = 0;
+ g_timer_start(mp->timer);
+
+ return title;
+}
+
+static gboolean track_changed(DBusConnection *connection, DBusMessage *msg,
+ void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBusMessageIter iter;
+
+ DBG("sender=%s path=%s", mp->sender, mp->path);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_player_metadata(mp, &iter) == FALSE) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ }
+
+ avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
+
+ return TRUE;
+}
+
+static struct media_player *media_player_create(struct media_adapter *adapter,
+ const char *sender,
+ const char *path,
+ int *err)
+{
+ struct media_player *mp;
+
+ mp = g_new0(struct media_player, 1);
+ mp->adapter = adapter;
+ mp->sender = g_strdup(sender);
+ mp->path = g_strdup(path);
+ mp->timer = g_timer_new();
+
+ mp->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+ media_player_exit, mp,
+ NULL);
+ mp->property_watch = g_dbus_add_signal_watch(adapter->conn, sender,
+ path, MEDIA_PLAYER_INTERFACE,
+ "PropertyChanged",
+ property_changed,
+ mp, NULL);
+ mp->track_watch = g_dbus_add_signal_watch(adapter->conn, sender,
+ path, MEDIA_PLAYER_INTERFACE,
+ "TrackChanged",
+ track_changed,
+ mp, NULL);
+ mp->player = avrcp_register_player(&adapter->src, &player_cb, mp,
+ media_player_free);
+ if (!mp->player) {
+ if (err)
+ *err = -EPROTONOSUPPORT;
+ media_player_destroy(mp);
+ return NULL;
+ }
+
+ adapter->players = g_slist_append(adapter->players, mp);
+
+ info("Player registered: sender=%s path=%s", sender, path);
+
+ if (err)
+ *err = 0;
+
+ return mp;
+}
+
+static gboolean parse_player_properties(struct media_player *mp,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (set_property(mp, key, &entry) == FALSE)
+ return FALSE;
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return TRUE;
+}
+
+static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ struct media_player *mp;
+ DBusMessageIter args;
+ const char *sender, *path;
+ int err;
+
+ sender = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+
+ if (media_adapter_find_player(adapter, sender, path) != NULL)
+ return btd_error_already_exists(msg);
+
+ mp = media_player_create(adapter, sender, path, &err);
+ if (mp == NULL) {
+ if (err == -EPROTONOSUPPORT)
+ return btd_error_not_supported(msg);
+ else
+ return btd_error_invalid_args(msg);
+ }
+
+ if (parse_player_properties(mp, &args) == FALSE) {
+ media_player_destroy(mp);
+ return btd_error_invalid_args(msg);
+ }
+
+ dbus_message_iter_next(&args);
+
+ if (parse_player_metadata(mp, &args) == FALSE) {
+ media_player_destroy(mp);
+ return btd_error_invalid_args(msg);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ struct media_player *player;
+ const char *sender, *path;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ player = media_adapter_find_player(adapter, sender, path);
+ if (player == NULL)
+ return btd_error_does_not_exist(msg);
+
+ media_player_remove(player);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
static GDBusMethodTable media_methods[] = {
{ "RegisterEndpoint", "oa{sv}", "", register_endpoint },
{ "UnregisterEndpoint", "o", "", unregister_endpoint },
+ { "RegisterPlayer", "oa{sv}a{sv}","", register_player },
+ { "UnregisterPlayer", "o", "", unregister_player },
{ },
};

diff --git a/doc/media-api.txt b/doc/media-api.txt
index b2f239a..af4cfa0 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -44,6 +44,169 @@ Methods void RegisterEndpoint(object endpoint, dict properties)

Unregister sender end point.

+ void RegisterPlayer(object player, dict properties,
+ dict metadata)
+
+ Register a media player object to sender, the sender
+ can register as many objets as it likes.
+
+ Note: If the sender disconnects its objects are
+ automatically unregistered.
+
+ Properties:
+
+ string Equalizer:
+
+ Possible values: "off" or "on"
+
+ string Repeat:
+
+ Possible values: "off", "singletrack",
+ "alltracks" or "group"
+
+ string Shuffle:
+
+ Possible values: "off", "alltracks" or
+ "group"
+
+ string Scan:
+
+ Possible values: "off", "alltracks" or
+ "group"
+
+ string Status:
+
+ Possible values: "playing", "stopped",
+ "paused",
+ "forward-seek",
+ "reverse-seek" or
+ "error"
+
+ uint32 Position
+
+ Playback position in milliseconds
+
+ Metadata:
+
+ string Title:
+
+ Track title name
+
+ string Artist:
+
+ Track artist name
+
+ string Album:
+
+ Track album name
+
+ string Genre:
+
+ Track genre name
+
+ uint32 NumberOfTracks:
+
+ Number of tracks in total
+
+ uint32 Number:
+
+ Track number
+
+ uint32 Duration:
+
+ Track duration in milliseconds
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotSupported
+
+ void UnregisterPlayer(object player)
+
+ Unregister sender media player.
+
+MediaPlayer hierarchy
+=====================
+
+Service unique name
+Interface org.bluez.MediaPlayer
+Object path freely definable
+
+Methods void SetProperty(string property, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write can be changed.
+
+ On success this will emit a PropertyChanged signal.
+
+Signals PropertyChanged(string setting, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+ TrackChanged(dict metadata)
+
+ Possible values:
+
+ string Title:
+
+ Track title name
+
+ string Artist:
+
+ Track artist name
+
+ string Album:
+
+ Track album name
+
+ string Genre:
+
+ Track genre name
+
+ uint32 NumberOfTracks:
+
+ Number of tracks in total
+
+ uint32 Number:
+
+ Track number
+
+ uint32 Duration:
+
+ Track duration in milliseconds
+
+ StatusChanged(string status, uint32 position)
+
+ Possible status: "playing", "stopped", "paused",
+ "forward-seek", "reverse-seek" or
+ "error"
+
+Properties string Equalizer [readwrite]
+
+ Possible values: "off" or "on"
+
+ string Repeat [readwrite]
+
+ Possible values: "off", "singletrack", "alltracks" or
+ "group"
+
+ string Shuffle [readwrite]
+
+ Possible values: "off", "alltracks" or "group"
+
+ string Scan [readwrite]
+
+ Possible values: "off", "alltracks" or "group"
+
+ string Status [readonly]
+
+ Possible status: "playing", "stopped", "paused",
+ "forward-seek", "reverse-seek" or
+ "error"
+
+ uint32 Position [readonly]
+
+ Playback position in milliseconds
+
MediaEndpoint hierarchy
=======================

--
1.7.6.4


2011-10-03 22:01:18

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 5/5] Remove test-media-player

From: Luiz Augusto von Dentz <[email protected]>

---
test/test-media-player | 108 ------------------------------------------------
1 files changed, 0 insertions(+), 108 deletions(-)
delete mode 100755 test/test-media-player

diff --git a/test/test-media-player b/test/test-media-player
deleted file mode 100755
index 712cf31..0000000
--- a/test/test-media-player
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import dbus
-from optparse import OptionParser, make_option
-
-USAGE = "Usage: %prog [options] <command> [args]"
-COMMANDS = """
-Commands:
- changetrack <bdaddr> <key> <value> [<key> <value> ...]
-
- changeplayback <bdaddr> status elapsed-time
- status: one of playing, stopped, paused, forward-seek, reverse-seek
- or error.
- elapsed-time: in milliseconds
-
- setproperty <bdaddr> property value
- setting: one of Equalizer, Repeat, Shuffle or Scan
- value: value correspondent to the setting specified
-"""
-
-class MyParser(OptionParser):
- def format_epilog(self, formatter):
- return self.epilog
-
-bus = dbus.SystemBus()
-
-manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
-
-option_list = [
- make_option("-i", "--device", action="store",
- type="string", dest="dev_id"),
- ]
-parser = MyParser(option_list=option_list, usage=USAGE, epilog=COMMANDS)
-
-(options, args) = parser.parse_args()
-
-if len(args) < 2:
- parser.print_help()
- sys.exit(1)
-
-if options.dev_id:
- adapter_path = manager.FindAdapter(options.dev_id)
-else:
- adapter_path = manager.DefaultAdapter()
-
-adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
- "org.bluez.Adapter")
-
-device = adapter.FindDevice(args[1])
-control = dbus.Interface(bus.get_object("org.bluez", device),
- "org.bluez.Control")
-mp = dbus.Interface(bus.get_object("org.bluez", device),
- "org.bluez.MediaPlayer")
-
-def handle_change_track(mp, args):
- if len(args) % 2 != 0:
- print("Don't know how to handle odd number of parameters")
- print(USAGE)
- sys.exit(1)
-
- d = dict()
- for i in range(2, len(args), 2):
- key = args[i]
- if key == "Title" or key == "Artist" or key == "Album" \
- or key == "Genre":
- d[key] = dbus.String(args[i + 1].decode(sys.stdin.encoding))
- elif key == "NumberOfTracks" or key == "TrackNumber" \
- or key == "TrackDuration":
- d[key] = dbus.UInt32(int(args[i + 1]))
- else:
- print("Unknown metadata: %s" % key)
- sys.exit(1)
-
- d = dbus.Dictionary(d, signature='sv')
- mp.ChangeTrack(d)
-
-def handle_change_playback(mp, args):
- if len(args) != 4:
- print(USAGE)
- sys.exit(1)
-
- status = dbus.String(args[2])
- elapsed = dbus.UInt32(long(args[3]))
-
- mp.ChangePlayback(status, elapsed)
-
-def handle_set_property(mp, args):
- if len(args) != 4:
- print(USAGE)
- sys.exit(1)
-
- prop = dbus.String(args[2])
- value = dbus.String(args[3])
-
- mp.SetProperty(prop, value)
-
-
-handlers = { 'changetrack': handle_change_track,
- 'changeplayback': handle_change_playback,
- 'setproperty': handle_set_property }
-
-if not args[0] in handlers:
- print("Unknown command -- %s" % argv[1])
- print(USAGE)
- sys.exit(1)
-
-handlers[args[0]](mp, args)
--
1.7.6.4


2011-10-03 22:01:17

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 4/5] Remove MediaPlayer interface from control-api.txt

From: Luiz Augusto von Dentz <[email protected]>

---
doc/control-api.txt | 47 -----------------------------------------------
1 files changed, 0 insertions(+), 47 deletions(-)

diff --git a/doc/control-api.txt b/doc/control-api.txt
index a7e5cbb..e3a13e9 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -93,50 +93,3 @@ Properties uint8 SubUnitID [readonly]
array{string} Capabilities [readonly]

List of Capabilities provided by the connected device.
-
-
-MediaPlayer hierarchy [experimental]
-=====================
-
-Service org.bluez
-Interface org.bluez.MediaPlayer
-Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-
-Methods void ChangePlayback(string status, uint32 elapsed)
-
- The status can be "playing", "stopped", "paused",
- "forward-seek", "reverse-seek" or "error". Elapsed is
- the position within the track in milliseconds.
-
- void ChangeTrack(dict metadata)
-
- Called to send the mandated TrackChange event and
- potential metadata information.
-
- Current defined metadata information are represented
- with the following keys:
-
- Title string (mandatory)
- Artist string
- Album string
- Genre string
- NumberOfTracks uint32
- TrackNumber uint32
- TrackDuration uint32 (in milliseconds)
-
- void SetProperty(string property, variant value)
-
- Called to set the media-player's properties
-
- Current defined properties are represented with the
- following keys and values:
-
- Equalizer off, on
- Repeat off, singletrack, alltracks, group
- Shuffle off, alltracks, group
- Scan off, alltracks, group
-
-Signals PropertyChanged(string setting, variant value)
-
- Called when one of the settings are changed by the
- remote device or to inform its battery status.
--
1.7.6.4