2011-09-30 13:10:39

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 0/4] MediaPlayer agent registration

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

Current interface using device object has some limitation as it was not
designed to track clients e.g. test-media-player disconnects after
using it, making it almost impossible to handle multiple clients
properly.

Another problem is that by using the device object it increase the
complexity of the clients since they have to discover the device object
to use it and check if it has AVRCP uuid to be able to use MediaPlayer
interface.

So this changes move the MediaPlayer to the clients, so they register
themselves using Media.RegisterPlayer and creates a more clear
separation between AVRCP Player and a system MediaPlayer.

Luiz Augusto von Dentz (4):
AVRCP: move MediaPlayer to adapter object
Add simple-player test script
Remove MediaPlayer interface from control-api.txt
Remove test-media-player

audio/avrcp.c | 824 +++++++++---------------------------------------
audio/avrcp.h | 67 ++++-
audio/device.c | 3 -
audio/device.h | 2 -
audio/manager.c | 7 -
audio/media.c | 800 ++++++++++++++++++++++++++++++++++++++++++++++
doc/control-api.txt | 47 ---
doc/media-api.txt | 163 ++++++++++
test/simple-player | 56 ++++
test/test-media-player | 108 -------
10 files changed, 1237 insertions(+), 840 deletions(-)
create mode 100755 test/simple-player
delete mode 100755 test/test-media-player

--
1.7.6.2



2011-10-01 00:51:58

by David Stockwell

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

Hi guys,

-----Original Message-----
From: Luiz Augusto von Dentz
Sent: Friday, September 30, 2011 8:10 AM
To: [email protected]
Subject: [PATCH BlueZ 1/4] AVRCP: move MediaPlayer to adapter object

+++++
I like this approach, and would like to try things out, but cannot seem to
access git.kernel.org. Do you have another git tree somewhere?

Also, any idea on when the next BlueZ release will come out?

Thanks in advance
DS
+++++

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 | 824
++++++++++-------------------------------------------
audio/avrcp.h | 67 ++++-
audio/device.c | 3 -
audio/device.h | 2 -
audio/manager.c | 7 -
audio/media.c | 800 +++++++++++++++++++++++++++++++++++++++++++++++++++
doc/media-api.txt | 163 +++++++++++
7 files changed, 1181 insertions(+), 685 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index ba04a12..872f7a8 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)
+ if (player->session)
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);
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.
@@ -651,7 +404,7 @@ static void mp_set_playback_status(struct media_player
*mp, uint8_t status,
* If @param id is not valid, -EINVAL is returned. If there's no such media
* attribute, -ENOENT 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,64 +425,35 @@ 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:
- if (mi->artist == NULL)
- return -ENOENT;
-
- valp = mi->artist;
- break;
- case MEDIA_INFO_ALBUM:
- if (mi->album == NULL)
- return -ENOENT;
-
- valp = mi->album;
- break;
- case MEDIA_INFO_GENRE:
- if (mi->genre == NULL)
- return -ENOENT;
+ DBG("Get media attribute: %u", id);

- valp = mi->genre;
- break;
- case MEDIA_INFO_TRACK:
- if (!mi->track)
- return -ENOENT;
-
- snprintf(valstr, 20, "%u", mi->track);
- valp = valstr;
- break;
- case MEDIA_INFO_N_TRACKS:
- if (!mi->ntracks)
- return -ENOENT;
+ value = player->cb->get_metadata(id, player->user_data);
+ if (value == NULL)
+ return -ENOENT;

- snprintf(valstr, 20, "%u", mi->ntracks);
- valp = valstr;
+ 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)
- return -ENOENT;
-
- snprintf(valstr, 20, "%u", mi->track_len);
- valp = valstr;
+ 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 -EINVAL;
}

- if (valp) {
- len = strlen(valp);
-
- if (len > maxlen)
- return -ENOBUFS;
-
- memcpy(elem->val, valp, len);
- } else {
- len = 0;
- }
-
elem->id = htonl(id);
elem->charset = htons(0x6A); /* Always use UTF-8 */
elem->len = htons(len);
@@ -738,57 +461,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 void player_set_attribute(struct avrcp_player *player,
uint8_t attr, uint8_t val)
{
DBG("Change attribute: %u %u", attr, val);

- mp->settings[attr] = val;
+ 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)
{
@@ -814,7 +502,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;
@@ -827,7 +515,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)
{
@@ -840,11 +528,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;
}
@@ -860,14 +548,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]);
@@ -890,7 +578,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)
{
@@ -913,8 +601,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) {
@@ -930,7 +619,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);

@@ -956,7 +645,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)
{
@@ -964,7 +653,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;

/*
@@ -982,13 +671,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]);
@@ -1017,7 +706,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)
{
@@ -1039,23 +728,10 @@ 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)
- 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);
+ player_set_attribute(player, attr, val);
}

if (len) {
@@ -1070,7 +746,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)
{
@@ -1090,7 +766,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)
{
@@ -1104,9 +780,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;
@@ -1117,14 +790,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);
@@ -1132,25 +804,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
@@ -1161,10 +836,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:
@@ -1179,8 +853,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);

@@ -1195,7 +869,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[] = {
@@ -1232,7 +906,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);
@@ -1268,7 +942,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);

@@ -1279,52 +953,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;
@@ -1347,8 +1025,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;
@@ -1393,7 +1069,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;
@@ -1414,16 +1090,14 @@ 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;
+ g_free(player);
}

void avrcp_unregister(const bdaddr_t *src)
@@ -1434,6 +1108,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);
@@ -1449,235 +1125,43 @@ void avrcp_unregister(const bdaddr_t *src)
avctp_remove_state_cb(avctp_id);
}

-static DBusMessage *mp_set_property(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;
- 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);
- }
-
- if (mi->title == NULL)
- return FALSE;
-
- return TRUE;
-}
-
-static DBusMessage *mp_change_track(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- 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..f47fefe 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);
+ void (*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..6f7d9d7 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,783 @@ 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 void 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;
+
+ 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;
+ }
+
+ 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);
+}
+
+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) {
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ return "";
+ 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;
+
+ if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->timer, NULL);
+ sec = (uint32_t) timedelta;
+
+ return (uint32_t) ((timedelta - sec) * 1000);
+}
+
+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; }
+
+ mp->status = val;
+ if (mp->status == val)
+ return TRUE;
+
+ 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);
+
+ if (!strcmp(key, "Title")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ title = TRUE;
+ } else if (!strcmp(key, "Artist")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "Album")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "Genre")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "NumberOfTracks")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else if (!strcmp(key, "Number")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else if (!strcmp(key, "Duration")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else {
+ 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);
+
+ avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
+
+ 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));
+ }
+
+ 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..7834e38 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",
+ "alltrack" 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"
+
+Properies string Equalizer [readwrite]
+
+ Possible values: "off" or "on"
+
+ string Repeat [readwrite]
+
+ Possible values: "off", "singletrack", "alltrack" 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.2

2011-09-30 13:10:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 3/4] 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.2


2011-09-30 13:10:43

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 4/4] 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.2


2011-09-30 13:10:41

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 2/4] 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.2


2011-09-30 13:10:40

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 1/4] 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 | 824 ++++++++++-------------------------------------------
audio/avrcp.h | 67 ++++-
audio/device.c | 3 -
audio/device.h | 2 -
audio/manager.c | 7 -
audio/media.c | 800 +++++++++++++++++++++++++++++++++++++++++++++++++++
doc/media-api.txt | 163 +++++++++++
7 files changed, 1181 insertions(+), 685 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index ba04a12..872f7a8 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)
+ if (player->session)
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);
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.
@@ -651,7 +404,7 @@ static void mp_set_playback_status(struct media_player *mp, uint8_t status,
* If @param id is not valid, -EINVAL is returned. If there's no such media
* attribute, -ENOENT 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,64 +425,35 @@ 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:
- if (mi->artist == NULL)
- return -ENOENT;
-
- valp = mi->artist;
- break;
- case MEDIA_INFO_ALBUM:
- if (mi->album == NULL)
- return -ENOENT;
-
- valp = mi->album;
- break;
- case MEDIA_INFO_GENRE:
- if (mi->genre == NULL)
- return -ENOENT;
+ DBG("Get media attribute: %u", id);

- valp = mi->genre;
- break;
- case MEDIA_INFO_TRACK:
- if (!mi->track)
- return -ENOENT;
-
- snprintf(valstr, 20, "%u", mi->track);
- valp = valstr;
- break;
- case MEDIA_INFO_N_TRACKS:
- if (!mi->ntracks)
- return -ENOENT;
+ value = player->cb->get_metadata(id, player->user_data);
+ if (value == NULL)
+ return -ENOENT;

- snprintf(valstr, 20, "%u", mi->ntracks);
- valp = valstr;
+ 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)
- return -ENOENT;
-
- snprintf(valstr, 20, "%u", mi->track_len);
- valp = valstr;
+ 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 -EINVAL;
}

- if (valp) {
- len = strlen(valp);
-
- if (len > maxlen)
- return -ENOBUFS;
-
- memcpy(elem->val, valp, len);
- } else {
- len = 0;
- }
-
elem->id = htonl(id);
elem->charset = htons(0x6A); /* Always use UTF-8 */
elem->len = htons(len);
@@ -738,57 +461,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 void player_set_attribute(struct avrcp_player *player,
uint8_t attr, uint8_t val)
{
DBG("Change attribute: %u %u", attr, val);

- mp->settings[attr] = val;
+ 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)
{
@@ -814,7 +502,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;
@@ -827,7 +515,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)
{
@@ -840,11 +528,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;
}
@@ -860,14 +548,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]);
@@ -890,7 +578,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)
{
@@ -913,8 +601,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) {
@@ -930,7 +619,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);

@@ -956,7 +645,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)
{
@@ -964,7 +653,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;

/*
@@ -982,13 +671,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]);
@@ -1017,7 +706,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)
{
@@ -1039,23 +728,10 @@ 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)
- 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);
+ player_set_attribute(player, attr, val);
}

if (len) {
@@ -1070,7 +746,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)
{
@@ -1090,7 +766,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)
{
@@ -1104,9 +780,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;
@@ -1117,14 +790,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);
@@ -1132,25 +804,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
@@ -1161,10 +836,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:
@@ -1179,8 +853,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);

@@ -1195,7 +869,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[] = {
@@ -1232,7 +906,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);
@@ -1268,7 +942,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);

@@ -1279,52 +953,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;
@@ -1347,8 +1025,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;
@@ -1393,7 +1069,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;
@@ -1414,16 +1090,14 @@ 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;
+ g_free(player);
}

void avrcp_unregister(const bdaddr_t *src)
@@ -1434,6 +1108,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);
@@ -1449,235 +1125,43 @@ void avrcp_unregister(const bdaddr_t *src)
avctp_remove_state_cb(avctp_id);
}

-static DBusMessage *mp_set_property(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;
- 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);
- }
-
- if (mi->title == NULL)
- return FALSE;
-
- return TRUE;
-}
-
-static DBusMessage *mp_change_track(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- 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..f47fefe 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);
+ void (*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..6f7d9d7 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,783 @@ 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 void 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;
+
+ 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;
+ }
+
+ 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);
+}
+
+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) {
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ return "";
+ 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;
+
+ if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->timer, NULL);
+ sec = (uint32_t) timedelta;
+
+ return (uint32_t) ((timedelta - sec) * 1000);
+}
+
+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; }
+
+ mp->status = val;
+ if (mp->status == val)
+ return TRUE;
+
+ 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);
+
+ if (!strcmp(key, "Title")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ title = TRUE;
+ } else if (!strcmp(key, "Artist")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "Album")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "Genre")) {
+ if (value->type != DBUS_TYPE_STRING) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.str);
+ } else if (!strcmp(key, "NumberOfTracks")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else if (!strcmp(key, "Number")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else if (!strcmp(key, "Duration")) {
+ if (value->type != DBUS_TYPE_UINT32) {
+ g_free(value);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&var, &value->value.num);
+ } else {
+ 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);
+
+ avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
+
+ 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));
+ }
+
+ 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..7834e38 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",
+ "alltrack" 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"
+
+Properies string Equalizer [readwrite]
+
+ Possible values: "off" or "on"
+
+ string Repeat [readwrite]
+
+ Possible values: "off", "singletrack", "alltrack" 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.2


2011-10-06 14:26:54

by Lucas De Marchi

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

Hi Andrei,

* Andrei Emeltchenko <[email protected]> [2011-10-06 16:52:59 +0300]:

> Hi Luiz,
>
> On Thu, Oct 06, 2011 at 04:35:26PM +0300, Luiz Augusto von Dentz wrote:
> > Hi Andrei,
> >
> > On Thu, Oct 6, 2011 at 3:24 PM, Andrei Emeltchenko
> > <[email protected]> wrote:
> > > Hi guys,
> > >
> > > On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
> > >> Hi Luiz,
> > >>
> > >> On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> > >> > This move the MediaPlayer registration to adapter object on Media
> > >> > interface so we can track players properly.
> > >> > ---
> > >> > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
> > >> > ?audio/avrcp.h ? ? | ? 67 ++++-
> > >> > ?audio/device.c ? ?| ? ?3 -
> > >> > ?audio/device.h ? ?| ? ?2 -
> > >> > ?audio/manager.c ? | ? ?7 -
> > >> > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >> > ?doc/media-api.txt | ?163 +++++++++++
> > >> > ?7 files changed, 1179 insertions(+), 687 deletions(-)
> > >>
> > >> All four patches have been applied. Thanks.
> > >
> > > Can it cause this:
> > >
> > > $ LANG=en make
> > > make --no-print-directory all-am
> > > ?CC ? ? audio/media.o
> > > cc1: warnings being treated as errors
> > > audio/media.c: In function 'get_setting':
> > > audio/media.c:1106:44: error: cast to pointer from integer of different
> > > size
> > > make[1]: *** [audio/media.o] Error 1
> > > make: *** [all] Error 2
> > >
> > > in the line below:
> > > ? ? ? ?<------8<-------------------------------------------------------------
> > > ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > > ? ? ? ?<------8<-------------------------------------------------------------
> > >
> > > Is patch below OK?
> > >
> > > @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
> > > *user_data)
> > >
> > > ? ? ? ?DBG("%s", attr_to_str(attr));
> > >
> > > - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > > + ? ? ? value = g_hash_table_lookup(mp->settings,
> > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));
> > > ? ? ? ?if (!value)
> > > ? ? ? ? ? ? ? ?return -EINVAL;
> >
> > Are you using 32 bits? At least Im running 64 bit and Im not seeing
> > this problem.
>
> Yes, 32 bits.
>
> I found this define:
>
> /usr/lib/i386-linux-gnu/glib-2.0/include/glibconfig.h:#define
> GUINT_TO_POINTER(u) ((gpointer) (u))

Do you confirm that changing this header as below fixes your issue?
GUINT_TO_POINTER(u) ((gpointer) (guint) (u))

In that case we should send the patch below to glib:



>From db167c52918835aba947bb1a23bed3f62502244a Mon Sep 17 00:00:00 2001
From: Lucas De Marchi <[email protected]>
Date: Thu, 6 Oct 2011 11:18:03 -0300
Subject: [PATCH] Fix casts on 32 bits

If we don't do the cast to the proper size in 32 bits, things like below
doesn't work:

uint8_t u = 20;
void *p;

p = GUINT_TO_POINTER(u);
---
configure.ac | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index e4235e0..95e3c06 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3361,8 +3361,8 @@ $ac_cv_sizeof_int)
gintptr_modifier='""'
gintptr_format='"i"'
guintptr_format='"u"'
- glib_gpi_cast=''
- glib_gpui_cast=''
+ glib_gpi_cast='gint'
+ glib_gpui_cast='guint'
;;
$ac_cv_sizeof_long)
glib_intptr_type_define=long
--
1.7.7


2011-10-06 14:12:46

by Lucas De Marchi

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

Hi Andrei,

On Thu, Oct 6, 2011 at 10:52 AM, Andrei Emeltchenko
<[email protected]> wrote:
> Hi Luiz,
>
> On Thu, Oct 06, 2011 at 04:35:26PM +0300, Luiz Augusto von Dentz wrote:
>> Hi Andrei,
>>
>> On Thu, Oct 6, 2011 at 3:24 PM, Andrei Emeltchenko
>> <[email protected]> wrote:
>> > Hi guys,
>> >
>> > On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
>> >> Hi Luiz,
>> >>
>> >> On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
>> >> > This move the MediaPlayer registration to adapter object on Media
>> >> > interface so we can track players properly.
>> >> > ---
>> >> > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
>> >> > ?audio/avrcp.h ? ? | ? 67 ++++-
>> >> > ?audio/device.c ? ?| ? ?3 -
>> >> > ?audio/device.h ? ?| ? ?2 -
>> >> > ?audio/manager.c ? | ? ?7 -
>> >> > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >> > ?doc/media-api.txt | ?163 +++++++++++
>> >> > ?7 files changed, 1179 insertions(+), 687 deletions(-)
>> >>
>> >> All four patches have been applied. Thanks.
>> >
>> > Can it cause this:
>> >
>> > $ LANG=en make
>> > make --no-print-directory all-am
>> > ?CC ? ? audio/media.o
>> > cc1: warnings being treated as errors
>> > audio/media.c: In function 'get_setting':
>> > audio/media.c:1106:44: error: cast to pointer from integer of different
>> > size
>> > make[1]: *** [audio/media.o] Error 1
>> > make: *** [all] Error 2
>> >
>> > in the line below:
>> > ? ? ? ?<------8<-------------------------------------------------------------
>> > ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
>> > ? ? ? ?<------8<-------------------------------------------------------------
>> >
>> > Is patch below OK?
>> >
>> > @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
>> > *user_data)
>> >
>> > ? ? ? ?DBG("%s", attr_to_str(attr));
>> >
>> > - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
>> > + ? ? ? value = g_hash_table_lookup(mp->settings,
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));
>> > ? ? ? ?if (!value)
>> > ? ? ? ? ? ? ? ?return -EINVAL;
>>
>> Are you using 32 bits? At least Im running 64 bit and Im not seeing
>> this problem.
>
> Yes, 32 bits.
>
> I found this define:
>
> /usr/lib/i386-linux-gnu/glib-2.0/include/glibconfig.h:#define
> GUINT_TO_POINTER(u) ? ? ((gpointer) ?(u))

Doc from glib 2.28 says it should have a cast to gulong. From glib's
source, we have this:

configure.ac:
case $ac_cv_sizeof_void_p in
$ac_cv_sizeof_int)
glib_intptr_type_define=int
gintptr_modifier='""'
gintptr_format='"i"'
guintptr_format='"u"'
glib_gpi_cast=''
glib_gpui_cast=''

...

#define GUINT_TO_POINTER(u) ((gpointer) ${glib_gpui_cast} (u))


I think this should be filed as a bug in glib. The fix would be to
make glib_gpui_cast='guint'



Lucas De Marchi

2011-10-06 13:52:59

by Andrei Emeltchenko

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

Hi Luiz,

On Thu, Oct 06, 2011 at 04:35:26PM +0300, Luiz Augusto von Dentz wrote:
> Hi Andrei,
>
> On Thu, Oct 6, 2011 at 3:24 PM, Andrei Emeltchenko
> <[email protected]> wrote:
> > Hi guys,
> >
> > On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
> >> Hi Luiz,
> >>
> >> On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> >> > This move the MediaPlayer registration to adapter object on Media
> >> > interface so we can track players properly.
> >> > ---
> >> > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
> >> > ?audio/avrcp.h ? ? | ? 67 ++++-
> >> > ?audio/device.c ? ?| ? ?3 -
> >> > ?audio/device.h ? ?| ? ?2 -
> >> > ?audio/manager.c ? | ? ?7 -
> >> > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> > ?doc/media-api.txt | ?163 +++++++++++
> >> > ?7 files changed, 1179 insertions(+), 687 deletions(-)
> >>
> >> All four patches have been applied. Thanks.
> >
> > Can it cause this:
> >
> > $ LANG=en make
> > make --no-print-directory all-am
> > ?CC ? ? audio/media.o
> > cc1: warnings being treated as errors
> > audio/media.c: In function 'get_setting':
> > audio/media.c:1106:44: error: cast to pointer from integer of different
> > size
> > make[1]: *** [audio/media.o] Error 1
> > make: *** [all] Error 2
> >
> > in the line below:
> > ? ? ? ?<------8<-------------------------------------------------------------
> > ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > ? ? ? ?<------8<-------------------------------------------------------------
> >
> > Is patch below OK?
> >
> > @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
> > *user_data)
> >
> > ? ? ? ?DBG("%s", attr_to_str(attr));
> >
> > - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > + ? ? ? value = g_hash_table_lookup(mp->settings,
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));
> > ? ? ? ?if (!value)
> > ? ? ? ? ? ? ? ?return -EINVAL;
>
> Are you using 32 bits? At least Im running 64 bit and Im not seeing
> this problem.

Yes, 32 bits.

I found this define:

/usr/lib/i386-linux-gnu/glib-2.0/include/glibconfig.h:#define
GUINT_TO_POINTER(u) ((gpointer) (u))

Best regards
Andrei Emeltchenko


2011-10-06 13:35:26

by Luiz Augusto von Dentz

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

Hi Andrei,

On Thu, Oct 6, 2011 at 3:24 PM, Andrei Emeltchenko
<[email protected]> wrote:
> Hi guys,
>
> On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
>> Hi Luiz,
>>
>> On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
>> > This move the MediaPlayer registration to adapter object on Media
>> > interface so we can track players properly.
>> > ---
>> > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
>> > ?audio/avrcp.h ? ? | ? 67 ++++-
>> > ?audio/device.c ? ?| ? ?3 -
>> > ?audio/device.h ? ?| ? ?2 -
>> > ?audio/manager.c ? | ? ?7 -
>> > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
>> > ?doc/media-api.txt | ?163 +++++++++++
>> > ?7 files changed, 1179 insertions(+), 687 deletions(-)
>>
>> All four patches have been applied. Thanks.
>
> Can it cause this:
>
> $ LANG=en make
> make --no-print-directory all-am
> ?CC ? ? audio/media.o
> cc1: warnings being treated as errors
> audio/media.c: In function 'get_setting':
> audio/media.c:1106:44: error: cast to pointer from integer of different
> size
> make[1]: *** [audio/media.o] Error 1
> make: *** [all] Error 2
>
> in the line below:
> ? ? ? ?<------8<-------------------------------------------------------------
> ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> ? ? ? ?<------8<-------------------------------------------------------------
>
> Is patch below OK?
>
> @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
> *user_data)
>
> ? ? ? ?DBG("%s", attr_to_str(attr));
>
> - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> + ? ? ? value = g_hash_table_lookup(mp->settings,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));
> ? ? ? ?if (!value)
> ? ? ? ? ? ? ? ?return -EINVAL;

Are you using 32 bits? At least Im running 64 bit and Im not seeing
this problem.

--
Luiz Augusto von Dentz

2011-10-06 12:43:11

by Andrei Emeltchenko

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

Hi Lucas,

On Thu, Oct 06, 2011 at 09:28:57AM -0300, Lucas De Marchi wrote:
> Hi Andrei,
>
> On Thu, Oct 6, 2011 at 9:24 AM, Andrei Emeltchenko
> <[email protected]> wrote:
> >
> > Hi guys,
> >
> > On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
> > > Hi Luiz,
> > >
> > > On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> > > > This move the MediaPlayer registration to adapter object on Media
> > > > interface so we can track players properly.
> > > > ---
> > > > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
> > > > ?audio/avrcp.h ? ? | ? 67 ++++-
> > > > ?audio/device.c ? ?| ? ?3 -
> > > > ?audio/device.h ? ?| ? ?2 -
> > > > ?audio/manager.c ? | ? ?7 -
> > > > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
> > > > ?doc/media-api.txt | ?163 +++++++++++
> > > > ?7 files changed, 1179 insertions(+), 687 deletions(-)
> > >
> > > All four patches have been applied. Thanks.
> >
> > Can it cause this:
> >
> > $ LANG=en make
> > make --no-print-directory all-am
> > ?CC ? ? audio/media.o
> > cc1: warnings being treated as errors
> > audio/media.c: In function 'get_setting':
> > audio/media.c:1106:44: error: cast to pointer from integer of different
> > size
> > make[1]: *** [audio/media.o] Error 1
> > make: *** [all] Error 2
> >
> > in the line below:
> > ? ? ? ?<------8<-------------------------------------------------------------
> > ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > ? ? ? ?<------8<-------------------------------------------------------------
> >
> > Is patch below OK?
> >
> > @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
> > *user_data)
> >
> > ? ? ? ?DBG("%s", attr_to_str(attr));
> >
> > - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> > + ? ? ? value = g_hash_table_lookup(mp->settings,
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));
>
> What glib are you using?

I use standard glib which comes with "Ubuntu 11.04 - the
Natty Narwhal - released in April 2011"

ii libglib2.0-dev 2.28.6-0ubuntu1 Development files for the GLib library

Best regards
Andrei Emeltchenko

>
> Recent glib has this implementation, which does not causes warnings here:
>
> #define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))
>
>
>
> regards,
> Lucas De Marchi

2011-10-06 12:28:57

by Lucas De Marchi

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

Hi Andrei,

On Thu, Oct 6, 2011 at 9:24 AM, Andrei Emeltchenko
<[email protected]> wrote:
>
> Hi guys,
>
> On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
> > Hi Luiz,
> >
> > On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> > > This move the MediaPlayer registration to adapter object on Media
> > > interface so we can track players properly.
> > > ---
> > > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------
> > > ?audio/avrcp.h ? ? | ? 67 ++++-
> > > ?audio/device.c ? ?| ? ?3 -
> > > ?audio/device.h ? ?| ? ?2 -
> > > ?audio/manager.c ? | ? ?7 -
> > > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
> > > ?doc/media-api.txt | ?163 +++++++++++
> > > ?7 files changed, 1179 insertions(+), 687 deletions(-)
> >
> > All four patches have been applied. Thanks.
>
> Can it cause this:
>
> $ LANG=en make
> make --no-print-directory all-am
> ?CC ? ? audio/media.o
> cc1: warnings being treated as errors
> audio/media.c: In function 'get_setting':
> audio/media.c:1106:44: error: cast to pointer from integer of different
> size
> make[1]: *** [audio/media.o] Error 1
> make: *** [all] Error 2
>
> in the line below:
> ? ? ? ?<------8<-------------------------------------------------------------
> ? ? ? ?| ?value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> ? ? ? ?<------8<-------------------------------------------------------------
>
> Is patch below OK?
>
> @@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
> *user_data)
>
> ? ? ? ?DBG("%s", attr_to_str(attr));
>
> - ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
> + ? ? ? value = g_hash_table_lookup(mp->settings,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER((uint32_t) attr));

What glib are you using?

Recent glib has this implementation, which does not causes warnings here:

#define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))



regards,
Lucas De Marchi

2011-10-06 12:24:26

by Andrei Emeltchenko

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

Hi guys,

On Thu, Oct 06, 2011 at 11:16:47AM +0300, Johan Hedberg wrote:
> Hi Luiz,
>
> On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> > This move the MediaPlayer registration to adapter object on Media
> > interface so we can track players properly.
> > ---
> > audio/avrcp.c | 840 +++++++++++------------------------------------------
> > audio/avrcp.h | 67 ++++-
> > audio/device.c | 3 -
> > audio/device.h | 2 -
> > audio/manager.c | 7 -
> > audio/media.c | 784 +++++++++++++++++++++++++++++++++++++++++++++++++
> > doc/media-api.txt | 163 +++++++++++
> > 7 files changed, 1179 insertions(+), 687 deletions(-)
>
> All four patches have been applied. Thanks.

Can it cause this:

$ LANG=en make
make --no-print-directory all-am
CC audio/media.o
cc1: warnings being treated as errors
audio/media.c: In function 'get_setting':
audio/media.c:1106:44: error: cast to pointer from integer of different
size
make[1]: *** [audio/media.o] Error 1
make: *** [all] Error 2

in the line below:
<------8<-------------------------------------------------------------
| value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
<------8<-------------------------------------------------------------

Is patch below OK?

@@ -1103,7 +1103,8 @@ static int get_setting(uint8_t attr, void
*user_data)

DBG("%s", attr_to_str(attr));

- value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ value = g_hash_table_lookup(mp->settings,
+ GUINT_TO_POINTER((uint32_t) attr));
if (!value)
return -EINVAL;


Best regards
Andrei Emeltchenko

2011-10-06 08:16:47

by Johan Hedberg

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

Hi Luiz,

On Wed, Oct 05, 2011, Luiz Augusto von Dentz wrote:
> This move the MediaPlayer registration to adapter object on Media
> interface so we can track players properly.
> ---
> audio/avrcp.c | 840 +++++++++++------------------------------------------
> audio/avrcp.h | 67 ++++-
> audio/device.c | 3 -
> audio/device.h | 2 -
> audio/manager.c | 7 -
> audio/media.c | 784 +++++++++++++++++++++++++++++++++++++++++++++++++
> doc/media-api.txt | 163 +++++++++++
> 7 files changed, 1179 insertions(+), 687 deletions(-)

All four patches have been applied. Thanks.

Johan

2011-10-06 06:49:59

by Luiz Augusto von Dentz

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

Hi Lucas,

On Thu, Oct 6, 2011 at 1:48 AM, Lucas De Marchi
<[email protected]> wrote:
>> +
>> + ? ? ? if (title == FALSE)
>> + ? ? ? ? ? ? ? return TRUE;
>
> return FALSE;

That is on purpose, I do not want this to be mandatory to the player
so it doesn't need to pretend anything if not playing and we should be
able to handle this in the daemon.


--
Luiz Augusto von Dentz

2011-10-05 22:48:47

by Lucas De Marchi

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

Hi Luiz,

On Wed, Oct 5, 2011 at 5:02 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
>
> 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 ? ? | ?840 +++++++++++------------------------------------------
> ?audio/avrcp.h ? ? | ? 67 ++++-
> ?audio/device.c ? ?| ? ?3 -
> ?audio/device.h ? ?| ? ?2 -
> ?audio/manager.c ? | ? ?7 -
> ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++
> ?doc/media-api.txt | ?163 +++++++++++
> ?7 files changed, 1179 insertions(+), 687 deletions(-)

I did an interdiff from your previous patch. I'm commenting only on
these changes. The others I think I had already reviewed all.


>
> diff --git a/audio/avrcp.c b/audio/avrcp.c
> index 6e8b8ff..96a0d36 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) < 0) {

good catch.

> ? ? ? ? ? ? ? ? ? ? ? ?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;
>
> ? ? ? ?/*
> @@ -977,16 +672,16 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
> ? ? ? ? * non-existent we should send an error.
> ? ? ? ? */
> ? ? ? ?for (i = 0; i < pdu->params[0]; i++) {
> - ? ? ? ? ? ? ? uint8_t val;
> + ? ? ? ? ? ? ? int 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]);
> - ? ? ? ? ? ? ? if (!val) {
> + ? ? ? ? ? ? ? val = player_get_attribute(player, settings[i]);
> + ? ? ? ? ? ? ? if (val < 0) {
> ? ? ? ? ? ? ? ? ? ? ? ?DBG("Ignoring %u: not supported by player",
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?settings[i]);
> ? ? ? ? ? ? ? ? ? ? ? ?continue;
> @@ -1014,14 +709,14 @@ 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)
> ?{
> ? ? ? ?uint16_t len = ntohs(pdu->params_len);
> ? ? ? ?unsigned int i;
>
> - ? ? ? if (len <= 3)
> + ? ? ? if (len < 3)
> ? ? ? ? ? ? ? ?goto err;
>
> ? ? ? ?len = 0;
> @@ -1033,26 +728,14 @@ static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
> ? ? ? ? * attribute is valid, we respond with no parameters. Otherwise an
> ? ? ? ? * E_INVALID_PARAM is sent.
> ? ? ? ? */
> - ? ? ? for (i = 1; i < pdu->params[0]; i += 2) {
> + ? ? ? for (i = 1; i <= pdu->params[0]; i += 2) {

Not the right fix. But I'm sending my patch on top of this, since the
bug was already there.

> ? ? ? ? ? ? ? ?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..66d09bc 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_OFF ? ? ? ? ? ?0x01
> +#define AVRCP_EQUALIZER_ON ? ? ? ? ? ? 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..9057d70 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,767 @@ 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));
> +
> + ? ? ? if (mp->track == NULL)
> + ? ? ? ? ? ? ? return NULL;

Good.

> +
> + ? ? ? 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->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;
> + ? ? ? 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);
> +
> + ? ? ? set_property(mp, property, &iter);
> +
> + ? ? ? 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);
> + ? ? ? }
> +
> + ? ? ? if (title == FALSE)
> + ? ? ? ? ? ? ? return TRUE;

return FALSE;


> +
> + ? ? ? mp->position = 0;
> + ? ? ? g_timer_start(mp->timer);
> +
> + ? ? ? avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
> +
> + ? ? ? return TRUE;
> +}
> +
> +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));
> + ? ? ? }
> +
> + ? ? ? 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
> ?=======================


The rest looks good.

Lucas De Marchi

2011-10-01 04:25:23

by Lucas De Marchi

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

Hi Luiz,

On Fri, Sep 30, 2011 at 10:10 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> 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 ? ? | ?824 ++++++++++-------------------------------------------
> ?audio/avrcp.h ? ? | ? 67 ++++-
> ?audio/device.c ? ?| ? ?3 -
> ?audio/device.h ? ?| ? ?2 -
> ?audio/manager.c ? | ? ?7 -
> ?audio/media.c ? ? | ?800 +++++++++++++++++++++++++++++++++++++++++++++++++++
> ?doc/media-api.txt | ?163 +++++++++++
> ?7 files changed, 1181 insertions(+), 685 deletions(-)
>


I'm going through the changes, spotting some problems... Some of them
are fixed by my previous patches. I'll just keep a note here for the
record.


> diff --git a/audio/avrcp.c b/audio/avrcp.c
> index ba04a12..872f7a8 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)
> + ? ? ? if (player->session)
> ? ? ? ? ? ? ? ?return -ENOTCONN;

This is fixed by "AVRCP: fix changed notification".

>
> - ? ? ? 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);

This is fixed by "AVRCP: fix missing bytes on notification"


> ? ? ? ?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.
> @@ -651,7 +404,7 @@ static void mp_set_playback_status(struct media_player *mp, uint8_t status,
> ?* If @param id is not valid, -EINVAL is returned. If there's no such media
> ?* attribute, -ENOENT 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,64 +425,35 @@ 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:
> - ? ? ? ? ? ? ? if (mi->artist == NULL)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> -
> - ? ? ? ? ? ? ? valp = mi->artist;
> - ? ? ? ? ? ? ? break;
> - ? ? ? case MEDIA_INFO_ALBUM:
> - ? ? ? ? ? ? ? if (mi->album == NULL)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> -
> - ? ? ? ? ? ? ? valp = mi->album;
> - ? ? ? ? ? ? ? break;
> - ? ? ? case MEDIA_INFO_GENRE:
> - ? ? ? ? ? ? ? if (mi->genre == NULL)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> + ? ? ? DBG("Get media attribute: %u", id);
>
> - ? ? ? ? ? ? ? valp = mi->genre;
> - ? ? ? ? ? ? ? break;
> - ? ? ? case MEDIA_INFO_TRACK:
> - ? ? ? ? ? ? ? if (!mi->track)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> -
> - ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->track);
> - ? ? ? ? ? ? ? valp = valstr;
> - ? ? ? ? ? ? ? break;
> - ? ? ? case MEDIA_INFO_N_TRACKS:
> - ? ? ? ? ? ? ? if (!mi->ntracks)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> + ? ? ? value = player->cb->get_metadata(id, player->user_data);
> + ? ? ? if (value == NULL)
> + ? ? ? ? ? ? ? return -ENOENT;

This changed a little bit from my fix on "AVRCP: return empty string
instead of rejecting". I think the best solution now is making
get_metadata() to return NULL (even for Title) and setting len=0 in
this case.

>
> - ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->ntracks);
> - ? ? ? ? ? ? ? valp = valstr;
> + ? ? ? 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)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOENT;
> -
> - ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->track_len);
> - ? ? ? ? ? ? ? valp = valstr;
> + ? ? ? 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);

wouldn't it be better to return metadata_value and accessing the union
instead of casting here?


> + ? ? ? ? ? ? ? if (len > maxlen)
> + ? ? ? ? ? ? ? ? ? ? ? return -ENOBUFS;
> + ? ? ? ? ? ? ? memcpy(elem->val, valstr, len);
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
>
> - ? ? ? if (valp) {
> - ? ? ? ? ? ? ? len = strlen(valp);
> -
> - ? ? ? ? ? ? ? if (len > maxlen)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOBUFS;
> -
> - ? ? ? ? ? ? ? memcpy(elem->val, valp, len);
> - ? ? ? } else {
> - ? ? ? ? ? ? ? len = 0;
> - ? ? ? }
> -
> ? ? ? ?elem->id = htonl(id);
> ? ? ? ?elem->charset = htons(0x6A); /* Always use UTF-8 */
> ? ? ? ?elem->len = htons(len);
> @@ -738,57 +461,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 void player_set_attribute(struct avrcp_player *player,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t attr, uint8_t val)
> ?{
> ? ? ? ?DBG("Change attribute: %u %u", attr, val);
>
> - ? ? ? mp->settings[attr] = val;
> + ? ? ? 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)
> ?{
> @@ -814,7 +502,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;
> @@ -827,7 +515,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)
> ?{
> @@ -840,11 +528,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)) {

Nice... when we have AVRCP 1.4, we might want to compare with a list
of supported settings that players must export.


> ? ? ? ? ? ? ? ? ? ? ? ?DBG("Ignoring setting %u: not supported by player", i);
> ? ? ? ? ? ? ? ? ? ? ? ?continue;
> ? ? ? ? ? ? ? ?}
> @@ -860,14 +548,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]);
> @@ -890,7 +578,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)
> ?{
> @@ -913,8 +601,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) {
> @@ -930,7 +619,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);
>
> @@ -956,7 +645,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)
> ?{
> @@ -964,7 +653,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;
>
> ? ? ? ?/*
> @@ -982,13 +671,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]);
> @@ -1017,7 +706,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)
> ?{
> @@ -1039,23 +728,10 @@ 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)
> - ? ? ? ? ? ? ? ? ? ? ? continue;
>
> ? ? ? ? ? ? ? ?len++;

Removing these "continue" you are not keeping track of how many
settings you changed. See below.

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

If there was no valid setting, we should return an error. A solution
here might be returning boolean on player_set_attribute() in case the
setting is supported.


> @@ -1070,7 +746,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)
> ?{
> @@ -1090,7 +766,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)
> ?{
> @@ -1104,9 +780,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);

Shouldn't you call a correpondent method in player? We are missing
functionality here, no?


> ? ? ? ?pdu->params_len = 0;
>
> ? ? ? ?return AVC_CTYPE_STABLE;
> @@ -1117,14 +790,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);
> @@ -1132,25 +804,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
> @@ -1161,10 +836,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:
> @@ -1179,8 +853,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);
>
> @@ -1195,7 +869,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[] = {
> @@ -1232,7 +906,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);
> @@ -1268,7 +942,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);
>
> @@ -1279,52 +953,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;
> @@ -1347,8 +1025,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;
> @@ -1393,7 +1069,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;
> @@ -1414,16 +1090,14 @@ 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;
> + ? ? ? g_free(player);
> ?}
>
> ?void avrcp_unregister(const bdaddr_t *src)
> @@ -1434,6 +1108,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);
> @@ -1449,235 +1125,43 @@ void avrcp_unregister(const bdaddr_t *src)
> ? ? ? ? ? ? ? ?avctp_remove_state_cb(avctp_id);
> ?}
>
> -static DBusMessage *mp_set_property(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;
> - ? ? ? 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);
> - ? ? ? }
> -
> - ? ? ? if (mi->title == NULL)
> - ? ? ? ? ? ? ? return FALSE;
> -
> - ? ? ? return TRUE;
> -}
> -
> -static DBusMessage *mp_change_track(DBusConnection *conn,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessage *msg, void *data)
> -{
> - ? ? ? 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..f47fefe 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);
> + ? ? ? void (*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..6f7d9d7 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,783 @@ 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 void 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;
> +
> + ? ? ? 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;
> + ? ? ? }
> +
> + ? ? ? 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);
> +}
> +
> +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) {
> + ? ? ? ? ? ? ? if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
> + ? ? ? ? ? ? ? ? ? ? ? return "";
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }

We will need to work a bit different here. See my patch

> +
> + ? ? ? 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;
> +
> + ? ? ? if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
> + ? ? ? ? ? ? ? return mp->position;
> +
> + ? ? ? timedelta = g_timer_elapsed(mp->timer, NULL);
> + ? ? ? sec = (uint32_t) timedelta;
> +
> + ? ? ? return (uint32_t) ((timedelta - sec) * 1000);


Why are you not taking the current position into account?


> +}
> +
> +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; ? }

Small style nit.

> +
> + ? ? ? mp->status = val;
> + ? ? ? if (mp->status == val)
> + ? ? ? ? ? ? ? return TRUE;

These lines are inverted and mp->status will be always equal val :-)

You also need to either stop the timer here or not take it into
consideration in get_position()


> +
> + ? ? ? 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);

Separating set_position() and set_status() makes the time accounting a
bit more difficult. If we keep this API, i think we will need the
following:

set_position() always starts the timer
set_status() always starts the timer
get_position() ignores the timer if in paused/stopped/error states


Maybe it's better to keep set_position()/set_status() together.
Application only has to pass the same status as before if the
intention is to change the position only. What do you think? Think
about the other events too: TRACK_REACHED_END and TRACK_REACHED_START.
While the later would be easy, the former would be a pain.


> + ? ? ? 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);
> +

Seems like the patch to unselect the track is still needed. I'll
rebase on top of this.

> + ? ? ? 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);

You are setting all the fields. You don't need to zero them.

> + ? ? ? ? ? ? ? value->type = dbus_message_iter_get_arg_type(&var);
> +
> + ? ? ? ? ? ? ? if (!strcmp(key, "Title")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_STRING) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.str);
> + ? ? ? ? ? ? ? ? ? ? ? title = TRUE;
> + ? ? ? ? ? ? ? } else if (!strcmp(key, "Artist")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_STRING) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.str);
> + ? ? ? ? ? ? ? } else if (!strcmp(key, "Album")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_STRING) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.str);
> + ? ? ? ? ? ? ? } else if (!strcmp(key, "Genre")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_STRING) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.str);


These ifs are all the same now... could we merge them?


> + ? ? ? ? ? ? ? } else if (!strcmp(key, "NumberOfTracks")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_UINT32) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.num);
> + ? ? ? ? ? ? ? } else if (!strcmp(key, "Number")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_UINT32) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.num);
> + ? ? ? ? ? ? ? } else if (!strcmp(key, "Duration")) {
> + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_UINT32) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.num);

same here.

> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? 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);
> + ? ? ? ? ? ? ? }

Starting from here... see below

> + ? ? ? ? ? ? ? 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);
> +
> + ? ? ? avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL);
> +

Probably better to move these last lines to track_changed() and keep
this function only parsing the dbus message.


> + ? ? ? 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));
> + ? ? ? }
> +
> + ? ? ? 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..7834e38 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",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "alltrack" 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"
> +
> +Properies ? ? ?string Equalizer [readwrite]

Properties

> +
> + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off" or "on"
> +
> + ? ? ? ? ? ? ? string Repeat [readwrite]
> +
> + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "singletrack", "alltrack" or
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "group"

alltracks

> +
> + ? ? ? ? ? ? ? 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
> ?=======================


I may have missed some issues... but it will need to be tested to find
them and it is not possible for me during the weekend.





regards,
Lucas De Marchi