2012-10-29 14:41:43

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 1/7] AVRCP: Add initial support for controller player

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

This also bump controller record to 1.3.
---
audio/avrcp.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 441 insertions(+), 90 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 57502ff..4f14513 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -190,6 +190,7 @@ struct avrcp {
int features;

void (*init) (struct avrcp *session);
+ void (*destroy) (struct avrcp *sesion);

const struct control_pdu_handler *control_handlers;

@@ -216,7 +217,7 @@ static uint32_t company_ids[] = {
IEEEID_BTSIG,
};

-static void register_notification(struct avrcp *session, uint8_t event);
+static void avrcp_register_notification(struct avrcp *session, uint8_t event);

static sdp_record_t *avrcp_ct_record(void)
{
@@ -227,7 +228,7 @@ static sdp_record_t *avrcp_ct_record(void)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -431,20 +432,12 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}

-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
- int value;
-
- DBG("attr %u", attr);
-
if (player == NULL)
return -ENOENT;

- value = player->cb->get_setting(attr, player->user_data);
- if (value < 0)
- DBG("attr %u not supported by player", attr);
-
- return value;
+ return player->cb->get_setting(id, player->user_data);
}

void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -490,7 +483,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;

- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;

@@ -643,14 +636,6 @@ static int player_set_setting(struct avrcp_player *player, uint8_t id,
return player->cb->set_setting(id, val, player->user_data);
}

-static int player_get_setting(struct avrcp_player *player, uint8_t id)
-{
- if (player == NULL)
- return -ENOENT;
-
- return player->cb->get_setting(id, player->user_data);
-}
-
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1095,7 +1080,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;

- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;

@@ -1357,6 +1342,319 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}

+static const char *status_to_string(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_PLAY_STATUS_STOPPED:
+ return "stopped";
+ case AVRCP_PLAY_STATUS_PLAYING:
+ return "playing";
+ case AVRCP_PLAY_STATUS_PAUSED:
+ return "paused";
+ case AVRCP_PLAY_STATUS_FWD_SEEK:
+ return "forward-seek";
+ case AVRCP_PLAY_STATUS_REV_SEEK:
+ return "reverse-seek";
+ case AVRCP_PLAY_STATUS_ERROR:
+ return "error";
+ default:
+ return NULL;
+ }
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint32_t duration;
+ uint32_t position;
+ uint8_t status;
+
+ if (code == AVC_CTYPE_REJECTED || ntohs(pdu->params_len) != 9)
+ return FALSE;
+
+ memcpy(&duration, pdu->params, sizeof(uint32_t));
+ duration = ntohl(duration);
+
+ memcpy(&position, pdu->params + 4, sizeof(uint32_t));
+ position = ntohl(position);
+
+ memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+ DBG("%s", status_to_string(status));
+
+ return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_get_play_status_rsp,
+ session);
+}
+
+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 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 gboolean avrcp_player_value_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (pdu->params_len < count * 2)
+ return FALSE;
+
+ for (i = 1; count > 0; count--, i += 2) {
+ const char *key;
+ const char *value;
+
+ key = attr_to_str(pdu->params[i]);
+ if (key == NULL)
+ continue;
+
+ value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+ if (value == NULL)
+ continue;
+
+ DBG("%s: %s", key, value);
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_current_player_value(struct avrcp *session,
+ uint8_t *attrs, uint8_t count)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 5];
+ struct avrcp_header *pdu = (void *) buf;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params_len = htons(count + 1);
+ pdu->params[0] = count;
+
+ for (i = 0; count > 0; count--, i++)
+ pdu->params[i + 1] = attrs[i];
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_player_value_rsp, session);
+}
+
+static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) < count) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ avrcp_get_current_player_value(session, &pdu->params[1],
+ pdu->params[0]);
+
+ return FALSE;
+}
+
+static void avrcp_list_player_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_list_player_attributes_rsp,
+ session);
+}
+
+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 gboolean avrcp_get_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) - 1 < count * 8) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ for (i = 1; count > 0; count--) {
+ uint32_t id;
+ uint16_t charset, len;
+
+ memcpy(&id, &pdu->params[i], sizeof(uint32_t));
+ id = ntohl(id);
+ i += sizeof(uint32_t);
+
+ memcpy(&charset, &pdu->params[i], sizeof(uint16_t));
+ charset = ntohs(charset);
+ i += sizeof(uint16_t);
+
+ memcpy(&len, &pdu->params[i], sizeof(uint16_t));
+ len = ntohs(len);
+ i += sizeof(uint16_t);
+
+ if (charset == 106) {
+ const char *key = metadata_to_str(id);
+ char *value = g_strndup((char *) &pdu->params[i], len);
+
+ DBG("%s: %s", key , value);
+ g_free(value);
+ }
+
+ i += len;
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_element_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+ pdu->params_len = htons(9);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_get_attributes_rsp,
+ session);
+}
+
static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1366,7 +1664,9 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
- uint8_t volume;
+ uint8_t value;
+ uint8_t count;
+ int i;

if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
@@ -1375,24 +1675,58 @@ static gboolean avrcp_handle_event(struct avctp *conn,

switch (event) {
case AVRCP_EVENT_VOLUME_CHANGED:
- volume = pdu->params[1] & 0x7F;
+ value = pdu->params[1] & 0x7F;

if (player)
- player->cb->set_volume(volume, session->dev,
+ player->cb->set_volume(value, session->dev,
player->user_data);

break;
+ case AVRCP_EVENT_STATUS_CHANGED:
+ value = pdu->params[1];
+
+ avrcp_get_play_status(session);
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ avrcp_get_element_attributes(session);
+
+ break;
+
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ count = pdu->params[1];
+
+ for (i = 2; count > 0; count--, i += 2) {
+ const char *key;
+ const char *value;
+
+ key = attr_to_str(pdu->params[i]);
+ if (key == NULL)
+ continue;
+
+ value = attrval_to_str(pdu->params[i],
+ pdu->params[i + 1]);
+ if (value == NULL)
+ continue;
+
+ DBG("%s: %s", key, value);
+ }
+
+ break;
}

if (code == AVC_CTYPE_CHANGED) {
- register_notification(session, event);
+ session->registered_events ^= (1 << event);
+ avrcp_register_notification(session, event);
return FALSE;
}

+ session->registered_events |= (1 << event);
+
return TRUE;
}

-static void register_notification(struct avrcp *session, uint8_t event)
+static void avrcp_register_notification(struct avrcp *session, uint8_t event)
{
uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
struct avrcp_header *pdu = (void *) buf;
@@ -1420,6 +1754,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
{
struct avrcp *session = user_data;
struct avrcp_header *pdu = (void *) operands;
+ uint16_t events = 0;
uint8_t count;

if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
@@ -1430,14 +1765,26 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
for (; count > 0; count--) {
uint8_t event = pdu->params[1 + count];

+ events |= (1 << event);
+
switch (event) {
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
- register_notification(session, event);
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ avrcp_register_notification(session, event);
break;
}
}

+ if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+ avrcp_list_player_attributes(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_play_status(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_element_attributes(session);
+
return FALSE;
}

@@ -1463,31 +1810,6 @@ static void avrcp_get_capabilities(struct avrcp *session)
session);
}

-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
-{
- return FALSE;
-}
-
-static void avrcp_get_play_status(struct avrcp *session)
-{
- uint8_t buf[AVRCP_HEADER_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
-
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_get_play_status_rsp,
- session);
-}
-
static struct avrcp *find_session(GSList *list, struct audio_device *dev)
{
for (; list; list = list->next) {
@@ -1516,7 +1838,8 @@ static void session_tg_init(struct avrcp *session)
session->control_handlers = tg_control_handlers;

if (session->version >= 0x0104) {
- register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
+ avrcp_register_notification(session,
+ AVRCP_EVENT_VOLUME_CHANGED);
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
@@ -1533,19 +1856,75 @@ static void session_tg_init(struct avrcp *session)

static void session_ct_init(struct avrcp *session)
{
+ struct avrcp_player *player;
+
session->control_handlers = ct_control_handlers;

DBG("%p version 0x%04x", session, session->version);

- if (session->version >= 0x0103) {
- avrcp_get_capabilities(session);
- avrcp_get_play_status(session);
- }
-
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
session);
+
+ if (session->version < 0x0103)
+ return;
+
+ player = g_new0(struct avrcp_player, 1);
+ player->sessions = g_slist_prepend(player->sessions, session);
+ session->player = player;
+
+ avrcp_get_capabilities(session);
+}
+
+static void session_destroy(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+ g_free(session);
+}
+
+static void session_tg_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player->sessions = g_slist_remove(player->sessions, session);
+
+ session_destroy(session);
+}
+
+static void player_destroy(gpointer data)
+{
+ struct avrcp_player *player = data;
+
+ if (player->destroy)
+ player->destroy(player->user_data);
+
+ g_slist_free(player->sessions);
+ g_free(player);
+}
+
+static void session_ct_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player_destroy(player);
+
+ session_destroy(session);
}

static struct avrcp *session_create(struct avrcp_server *server,
@@ -1574,9 +1953,11 @@ static struct avrcp *session_create(struct avrcp_server *server,

if (session->target) {
session->init = session_tg_init;
+ session->destroy = session_tg_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
} else {
session->init = session_ct_init;
+ session->destroy = session_ct_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
}

@@ -1595,25 +1976,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
return session;
}

-static void session_destroy(struct avrcp *session)
-{
- struct avrcp_server *server = session->server;
- struct avrcp_player *player = session->player;
-
- server->sessions = g_slist_remove(server->sessions, session);
-
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
-
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(session->browsing_id);
-
- if (player != NULL)
- player->sessions = g_slist_remove(player->sessions, session);
-
- g_free(session);
-}
-
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
avctp_state_t new_state, void *user_data)
{
@@ -1631,7 +1993,7 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;

- session_destroy(session);
+ session->destroy(session);

break;
case AVCTP_STATE_CONNECTING:
@@ -1740,17 +2102,6 @@ int avrcp_register(const bdaddr_t *src, GKeyFile *config)
return 0;
}

-static void player_destroy(gpointer data)
-{
- struct avrcp_player *player = data;
-
- if (player->destroy)
- player->destroy(player->user_data);
-
- g_slist_free(player->sessions);
- g_free(player);
-}
-
void avrcp_unregister(const bdaddr_t *src)
{
struct avrcp_server *server;
--
1.7.11.7



2012-10-31 08:39:37

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH BlueZ 1/7] AVRCP: Add initial support for controller player

Hi Luiz,

On Mon, Oct 29, 2012, Luiz Augusto von Dentz wrote:
> This also bump controller record to 1.3.
> ---
> audio/avrcp.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++----------
> 1 file changed, 441 insertions(+), 90 deletions(-)

This patch set has been applied. Thanks.

Johan

2012-10-30 08:49:12

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 3/7] player: Add support for SetProperty

Hi Lucas,

On Mon, Oct 29, 2012 at 4:41 PM, Lucas De Marchi
<[email protected]> wrote:
> On Mon, Oct 29, 2012 at 12:41 PM, Luiz Augusto von Dentz
> <[email protected]> wrote:
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> Properties Equalizer, Repeat, Shuffle and Scan can be set by user
>> application.
>> ---
>> audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
>> audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>> audio/player.h | 9 ++++
>> 3 files changed, 288 insertions(+), 4 deletions(-)
>>
>> diff --git a/audio/avrcp.c b/audio/avrcp.c
>> index 7c26491..724e139 100644
>> --- a/audio/avrcp.c
>> +++ b/audio/avrcp.c
>> @@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
>> 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 int attr_to_val(const char *str)
>> {
>> if (!strcasecmp(str, "Equalizer"))
>> @@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
>> session);
>> }
>>
>> +static const char *status_to_str(uint8_t status)
>> +{
>> + switch (status) {
>> + case AVRCP_STATUS_INVALID_COMMAND:
>> + return "Invalid Command";
>> + case AVRCP_STATUS_INVALID_PARAM:
>> + return "Invalid Parameter";
>> + case AVRCP_STATUS_INTERNAL_ERROR:
>> + return "Internal Error";
>> + case AVRCP_STATUS_SUCCESS:
>> + return "Success";
>> + default:
>> + return "Unknown";
>> + }
>> +}
>> +
>> static gboolean avrcp_player_value_rsp(struct avctp *conn,
>> uint8_t code, uint8_t subunit,
>> uint8_t *operands, size_t operand_count,
>> @@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
>> uint8_t count;
>> int i;
>>
>> - if (code == AVC_CTYPE_REJECTED)
>> + if (code == AVC_CTYPE_REJECTED) {
>> + media_player_set_setting(mp, "Error",
>> + status_to_str(pdu->params[0]));
>> return FALSE;
>> + }
>>
>> count = pdu->params[0];
>>
>> @@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
>> avrcp_handle_event, session);
>> }
>>
>> +static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
>> + uint8_t val)
>> +{
>> + uint8_t buf[AVRCP_HEADER_LENGTH + 3];
>> + struct avrcp_header *pdu = (void *) buf;
>> + uint8_t length;
>> +
>> + memset(buf, 0, sizeof(buf));
>> +
>> + set_company_id(pdu->company_id, IEEEID_BTSIG);
>> + pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
>> + pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
>> + pdu->params[0] = 1;
>> + pdu->params[1] = attr;
>> + pdu->params[2] = val;
>> + pdu->params_len = htons(3);
>> +
>> + length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
>> +
>> + avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
>> + AVC_SUBUNIT_PANEL, buf, length,
>> + avrcp_player_value_rsp, session);
>> +}
>> +
>> +static bool ct_set_setting(struct media_player *mp, const char *key,
>> + const char *value, void *user_data)
>> +{
>> + struct avrcp_player *player = user_data;
>> + int attr = attr_to_val(key);
>> + int val = attrval_to_val(attr, value);
>> + struct avrcp *session;
>> +
>> + session = player->sessions->data;
>> + if (session == NULL)
>> + return false;
>> +
>> + attr = attr_to_val(key);
>> + if (attr < 0)
>> + return false;
>> +
>> + val = attrval_to_val(attr, value);
>> + if (val < 0)
>> + return false;
>> +
>> + avrcp_set_player_value(session, attr, val);
>> +
>> + return true;
>> +}
>> +
>> +static const struct media_player_callback ct_cbs = {
>> + .set_setting = ct_set_setting,
>> +};
>> +
>> static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
>> uint8_t code, uint8_t subunit,
>> uint8_t *operands, size_t operand_count,
>> @@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
>>
>> path = device_get_path(session->dev->btd_dev);
>> mp = media_player_controller_create(path);
>> + media_player_set_callbacks(mp, &ct_cbs, player);
>> player->user_data = mp;
>> player->destroy = (GDestroyNotify) media_player_destroy;
>>
>> diff --git a/audio/player.c b/audio/player.c
>> index 1957594..e83b761 100644
>> --- a/audio/player.c
>> +++ b/audio/player.c
>> @@ -49,6 +49,12 @@ struct player_callback {
>> void *user_data;
>> };
>>
>> +struct pending_req {
>> + DBusMessage *msg;
>> + const char *key;
>> + const char *value;
>> +};
>> +
>> struct media_player {
>> char *path; /* Player object path */
>> GHashTable *settings; /* Player settings */
>> @@ -58,6 +64,7 @@ struct media_player {
>> GTimer *progress;
>> guint process_id;
>> struct player_callback *cb;
>> + GSList *pending;
>> };
>>
>> static void append_settings(void *key, void *value, void *user_data)
>> @@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
>> return reply;
>> }
>>
>> +static struct pending_req *find_pending(struct media_player *mp,
>> + const char *key)
>> +{
>> + GSList *l;
>> +
>> + for (l = mp->pending; l; l = l->next) {
>> + struct pending_req *p = l->data;
>> +
>> + if (strcasecmp(key, p->key) == 0)
>> + return p;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static struct pending_req *pending_new(DBusMessage *msg, const char *key,
>> + const char *value)
>> +{
>> + struct pending_req *p;
>> +
>> + p = g_new0(struct pending_req, 1);
>> + p->msg = dbus_message_ref(msg);
>> + p->key = key;
>> + p->value = value;
>> +
>> + return p;
>> +}
>> +
>> +static DBusMessage *player_set_setting(struct media_player *mp,
>> + DBusMessage *msg, const char *key,
>> + const char *value)
>> +{
>> + struct player_callback *cb = mp->cb;
>> + struct pending_req *p;
>> +
>> + if (cb == NULL || cb->cbs->set_setting == NULL)
>> + return btd_error_not_supported(msg);
>> +
>> + p = find_pending(mp, key);
>> + if (p != NULL)
>> + return btd_error_in_progress(msg);
>> +
>> + if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
>> + return btd_error_invalid_args(msg);
>> +
>> + p = pending_new(msg, key, value);
>> +
>> + mp->pending = g_slist_append(mp->pending, p);
>> +
>> + return NULL;
>> +}
>> +
>> static DBusMessage *media_player_set_property(DBusConnection *conn,
>> DBusMessage *msg, void *data)
>> {
>> - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
>> + struct media_player *mp = data;
>> + DBusMessageIter iter;
>> + DBusMessageIter var;
>> + const char *key, *value, *curval;
>> +
>> + 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, &key);
>> + 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);
>> +
>> + if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
>> + return btd_error_invalid_args(msg);
>> +
>> + dbus_message_iter_get_basic(&var, &value);
>> +
>> + if (g_strcmp0(key, "Equalizer") != 0 &&
>> + g_strcmp0(key, "Repeat") != 0 &&
>> + g_strcmp0(key, "Shuffle") != 0 &&
>> + g_strcmp0(key, "Scan") != 0)
>> + return btd_error_invalid_args(msg);
>> +
>> + curval = g_hash_table_lookup(mp->settings, key);
>> + if (g_strcmp0(curval, value) == 0)
>> + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
>> +
>> + return player_set_setting(mp, msg, key, value);
>> }
>>
>> static const GDBusMethodTable media_player_methods[] = {
>> @@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
>> { GDBUS_METHOD("GetTrack",
>> NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
>> media_player_get_track) },
>> - { GDBUS_METHOD("SetProperty",
>> + { GDBUS_ASYNC_METHOD("SetProperty",
>
> Why not using the DBus.Properties interface?

This is to align with what we currently have for target players, Im
planning to move everything related to MediaPlayer interface to
player.c then we can convert to use proper properties and change the
related tools such as mpris-player/simple-player.

--
Luiz Augusto von Dentz

2012-10-29 15:41:51

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH BlueZ 3/7] player: Add support for SetProperty

On Mon, Oct 29, 2012 at 12:41 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> Properties Equalizer, Repeat, Shuffle and Scan can be set by user
> application.
> ---
> audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
> audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> audio/player.h | 9 ++++
> 3 files changed, 288 insertions(+), 4 deletions(-)
>
> diff --git a/audio/avrcp.c b/audio/avrcp.c
> index 7c26491..724e139 100644
> --- a/audio/avrcp.c
> +++ b/audio/avrcp.c
> @@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
> 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 int attr_to_val(const char *str)
> {
> if (!strcasecmp(str, "Equalizer"))
> @@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
> session);
> }
>
> +static const char *status_to_str(uint8_t status)
> +{
> + switch (status) {
> + case AVRCP_STATUS_INVALID_COMMAND:
> + return "Invalid Command";
> + case AVRCP_STATUS_INVALID_PARAM:
> + return "Invalid Parameter";
> + case AVRCP_STATUS_INTERNAL_ERROR:
> + return "Internal Error";
> + case AVRCP_STATUS_SUCCESS:
> + return "Success";
> + default:
> + return "Unknown";
> + }
> +}
> +
> static gboolean avrcp_player_value_rsp(struct avctp *conn,
> uint8_t code, uint8_t subunit,
> uint8_t *operands, size_t operand_count,
> @@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
> uint8_t count;
> int i;
>
> - if (code == AVC_CTYPE_REJECTED)
> + if (code == AVC_CTYPE_REJECTED) {
> + media_player_set_setting(mp, "Error",
> + status_to_str(pdu->params[0]));
> return FALSE;
> + }
>
> count = pdu->params[0];
>
> @@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
> avrcp_handle_event, session);
> }
>
> +static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
> + uint8_t val)
> +{
> + uint8_t buf[AVRCP_HEADER_LENGTH + 3];
> + struct avrcp_header *pdu = (void *) buf;
> + uint8_t length;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + set_company_id(pdu->company_id, IEEEID_BTSIG);
> + pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
> + pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
> + pdu->params[0] = 1;
> + pdu->params[1] = attr;
> + pdu->params[2] = val;
> + pdu->params_len = htons(3);
> +
> + length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
> +
> + avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
> + AVC_SUBUNIT_PANEL, buf, length,
> + avrcp_player_value_rsp, session);
> +}
> +
> +static bool ct_set_setting(struct media_player *mp, const char *key,
> + const char *value, void *user_data)
> +{
> + struct avrcp_player *player = user_data;
> + int attr = attr_to_val(key);
> + int val = attrval_to_val(attr, value);
> + struct avrcp *session;
> +
> + session = player->sessions->data;
> + if (session == NULL)
> + return false;
> +
> + attr = attr_to_val(key);
> + if (attr < 0)
> + return false;
> +
> + val = attrval_to_val(attr, value);
> + if (val < 0)
> + return false;
> +
> + avrcp_set_player_value(session, attr, val);
> +
> + return true;
> +}
> +
> +static const struct media_player_callback ct_cbs = {
> + .set_setting = ct_set_setting,
> +};
> +
> static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
> uint8_t code, uint8_t subunit,
> uint8_t *operands, size_t operand_count,
> @@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
>
> path = device_get_path(session->dev->btd_dev);
> mp = media_player_controller_create(path);
> + media_player_set_callbacks(mp, &ct_cbs, player);
> player->user_data = mp;
> player->destroy = (GDestroyNotify) media_player_destroy;
>
> diff --git a/audio/player.c b/audio/player.c
> index 1957594..e83b761 100644
> --- a/audio/player.c
> +++ b/audio/player.c
> @@ -49,6 +49,12 @@ struct player_callback {
> void *user_data;
> };
>
> +struct pending_req {
> + DBusMessage *msg;
> + const char *key;
> + const char *value;
> +};
> +
> struct media_player {
> char *path; /* Player object path */
> GHashTable *settings; /* Player settings */
> @@ -58,6 +64,7 @@ struct media_player {
> GTimer *progress;
> guint process_id;
> struct player_callback *cb;
> + GSList *pending;
> };
>
> static void append_settings(void *key, void *value, void *user_data)
> @@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
> return reply;
> }
>
> +static struct pending_req *find_pending(struct media_player *mp,
> + const char *key)
> +{
> + GSList *l;
> +
> + for (l = mp->pending; l; l = l->next) {
> + struct pending_req *p = l->data;
> +
> + if (strcasecmp(key, p->key) == 0)
> + return p;
> + }
> +
> + return NULL;
> +}
> +
> +static struct pending_req *pending_new(DBusMessage *msg, const char *key,
> + const char *value)
> +{
> + struct pending_req *p;
> +
> + p = g_new0(struct pending_req, 1);
> + p->msg = dbus_message_ref(msg);
> + p->key = key;
> + p->value = value;
> +
> + return p;
> +}
> +
> +static DBusMessage *player_set_setting(struct media_player *mp,
> + DBusMessage *msg, const char *key,
> + const char *value)
> +{
> + struct player_callback *cb = mp->cb;
> + struct pending_req *p;
> +
> + if (cb == NULL || cb->cbs->set_setting == NULL)
> + return btd_error_not_supported(msg);
> +
> + p = find_pending(mp, key);
> + if (p != NULL)
> + return btd_error_in_progress(msg);
> +
> + if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
> + return btd_error_invalid_args(msg);
> +
> + p = pending_new(msg, key, value);
> +
> + mp->pending = g_slist_append(mp->pending, p);
> +
> + return NULL;
> +}
> +
> static DBusMessage *media_player_set_property(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> + struct media_player *mp = data;
> + DBusMessageIter iter;
> + DBusMessageIter var;
> + const char *key, *value, *curval;
> +
> + 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, &key);
> + 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);
> +
> + if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
> + return btd_error_invalid_args(msg);
> +
> + dbus_message_iter_get_basic(&var, &value);
> +
> + if (g_strcmp0(key, "Equalizer") != 0 &&
> + g_strcmp0(key, "Repeat") != 0 &&
> + g_strcmp0(key, "Shuffle") != 0 &&
> + g_strcmp0(key, "Scan") != 0)
> + return btd_error_invalid_args(msg);
> +
> + curval = g_hash_table_lookup(mp->settings, key);
> + if (g_strcmp0(curval, value) == 0)
> + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> +
> + return player_set_setting(mp, msg, key, value);
> }
>
> static const GDBusMethodTable media_player_methods[] = {
> @@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
> { GDBUS_METHOD("GetTrack",
> NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
> media_player_get_track) },
> - { GDBUS_METHOD("SetProperty",
> + { GDBUS_ASYNC_METHOD("SetProperty",

Why not using the DBus.Properties interface?

Lucas De Marchi

2012-10-29 14:41:46

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 4/7] AVRCP: Remove conversions inside media.c

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

The conversion should be done in avrcp.c as it is already doing for
controller players.
---
audio/avrcp.c | 37 ++++++-
audio/avrcp.h | 9 +-
audio/media.c | 315 ++++++++++------------------------------------------------
3 files changed, 89 insertions(+), 272 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 724e139..3d9ecc7 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -519,10 +519,19 @@ static int attr_to_val(const char *str)

static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
+ const char *key;
+ const char *value;
+
if (player == NULL)
return -ENOENT;

- return player->cb->get_setting(id, player->user_data);
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = player->cb->get_setting(key, player->user_data);
+
+ return attrval_to_val(id, value);
}

static int play_status_to_val(const char *status)
@@ -652,8 +661,14 @@ static const char *metadata_to_str(uint32_t id)
static const char *player_get_metadata(struct avrcp_player *player,
uint32_t id)
{
+ const char *key;
+
+ key = metadata_to_str(id);
+ if (key == NULL)
+ return NULL;
+
if (player != NULL)
- return player->cb->get_metadata(id, player->user_data);
+ return player->cb->get_metadata(key, player->user_data);

if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";
@@ -803,10 +818,20 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
static int player_set_setting(struct avrcp_player *player, uint8_t id,
uint8_t val)
{
+ const char *key, *value;
+
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = attrval_to_str(id, val);
+ if (value == NULL)
+ return -EINVAL;
+
if (player == NULL)
return -ENOENT;

- return player->cb->set_setting(id, val, player->user_data);
+ return player->cb->set_setting(key, value, player->user_data);
}

static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -1160,10 +1185,14 @@ static uint32_t player_get_duration(struct avrcp_player *player)

static uint8_t player_get_status(struct avrcp_player *player)
{
+ const char *value;
+
if (player == NULL)
return AVRCP_PLAY_STATUS_STOPPED;

- return player->cb->get_status(player->user_data);
+ value = player->cb->get_status(player->user_data);
+
+ return play_status_to_val(value);
}

static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 7f54adb..e607fb1 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -79,12 +79,13 @@

struct avrcp_player_cb {
GList *(*list_settings) (void *user_data);
- int (*get_setting) (uint8_t attr, void *user_data);
- int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+ const char *(*get_setting) (const char *key, void *user_data);
+ int (*set_setting) (const char *key, const char *value,
+ void *user_data);
uint64_t (*get_uid) (void *user_data);
- const char *(*get_metadata) (uint32_t id, void *user_data);
+ const char *(*get_metadata) (const char *key, void *user_data);
GList *(*list_metadata) (void *user_data);
- uint8_t (*get_status) (void *user_data);
+ const char *(*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
uint32_t (*get_duration) (void *user_data);
void (*set_volume) (uint8_t volume, struct audio_device *dev,
diff --git a/audio/media.c b/audio/media.c
index 7abf077..cec938a 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -98,7 +98,7 @@ struct media_player {
guint watch;
guint property_watch;
guint track_watch;
- uint8_t status;
+ char *status;
uint32_t position;
uint32_t duration;
uint8_t volume;
@@ -980,6 +980,7 @@ static void media_player_free(gpointer data)
g_timer_destroy(mp->timer);
g_free(mp->sender);
g_free(mp->path);
+ g_free(mp->status);
g_free(mp);
}

@@ -1007,193 +1008,6 @@ static void media_player_remove(struct media_player *mp)
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 GList *list_settings(void *user_data)
{
struct media_player *mp = user_data;
@@ -1206,38 +1020,24 @@ static GList *list_settings(void *user_data)
return g_hash_table_get_keys(mp->settings);
}

-static int get_setting(uint8_t attr, void *user_data)
+static const char *get_setting(const char *key, void *user_data)
{
struct media_player *mp = user_data;
- guint attr_uint = attr;
- void *value;
-
- DBG("%s", attr_to_str(attr));

- value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint));
- if (!value)
- return -EINVAL;
+ DBG("%s", key);

- return GPOINTER_TO_UINT(value);
+ return g_hash_table_lookup(mp->settings, key);
}

-static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+static int set_setting(const char *key, const char *value, void *user_data)
{
struct media_player *mp = user_data;
- const char *property, *value;
- guint attr_uint = attr;
DBusMessage *msg;
DBusMessageIter iter, var;

- property = attr_to_str(attr);
- value = attrval_to_str(attr, val);
-
- DBG("%s = %s", property, value);
+ DBG("%s = %s", key, value);

- if (property == NULL || value == NULL)
- return -EINVAL;
-
- if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)))
+ if (!g_hash_table_lookup(mp->settings, key))
return -EINVAL;

msg = dbus_message_new_method_call(mp->sender, mp->path,
@@ -1249,7 +1049,7 @@ static int set_setting(uint8_t attr, uint8_t val, void *user_data)
}

dbus_message_iter_init_append(msg, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);

dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING,
@@ -1286,19 +1086,19 @@ static uint64_t get_uid(void *user_data)
return 0;
}

-static const char *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(const char *key, void *user_data)
{
struct media_player *mp = user_data;

- DBG("%s", metadata_to_str(id));
+ DBG("%s", key);

if (mp->track == NULL)
return NULL;

- return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+ return g_hash_table_lookup(mp->track, key);
}

-static uint8_t get_status(void *user_data)
+static const char *get_status(void *user_data)
{
struct media_player *mp = user_data;

@@ -1311,7 +1111,7 @@ static uint32_t get_position(void *user_data)
double timedelta;
uint32_t sec, msec;

- if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ if (g_strcmp0(mp->status, "playing") != 0)
return mp->position;

timedelta = g_timer_elapsed(mp->timer, NULL);
@@ -1379,7 +1179,6 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
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;
@@ -1387,21 +1186,16 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
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)
+ if (g_strcmp0(mp->status, value) == 0)
return TRUE;

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

- mp->status = val;
+ g_free(mp->status);
+ mp->status = g_strdup(value);

- avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);

return TRUE;
}
@@ -1441,7 +1235,6 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
{
DBusMessageIter var;
const char *value, *curval;
- int attr, val;
GList *settings;

if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
@@ -1455,27 +1248,18 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
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;
-
- curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
return TRUE;

DBG("%s=%s", key, value);

- g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
- GUINT_TO_POINTER(val));
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));

settings = list_settings(mp);

@@ -1529,7 +1313,7 @@ static gboolean parse_player_metadata(struct media_player *mp,

dbus_message_iter_recurse(iter, &dict);

- track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+ track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
g_free);

while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
@@ -1540,7 +1324,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
char valstr[20];
char *value;
uint32_t num;
- int id;
int type;

if (ctype != DBUS_TYPE_DICT_ENTRY)
@@ -1553,10 +1336,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);

- id = metadata_to_val(key);
- if (id < 0)
- goto parse_error;
-
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
goto parse_error;

@@ -1564,36 +1343,46 @@ static gboolean parse_player_metadata(struct media_player *mp,

type = dbus_message_iter_get_arg_type(&var);

- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ if (strcasecmp(key, "Title") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
title = TRUE;
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Artist") == 0) {
if (type != DBUS_TYPE_STRING)
goto parse_error;

dbus_message_iter_get_basic(&var, &string);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ } else if (strcasecmp(key, "Album") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Genre") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Duration") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;

dbus_message_iter_get_basic(&var, &num);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ mp->duration = num;
+ } else if (strcasecmp(key, "Track") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;

dbus_message_iter_get_basic(&var, &num);
- mp->duration = num;
- break;
- default:
+ } else if (strcasecmp(key, "NumberOfTracks") == 0) {
+ if (type != DBUS_TYPE_UINT32)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &num);
+ } else
goto parse_error;
- }

- switch (dbus_message_iter_get_arg_type(&var)) {
+ switch (type) {
case DBUS_TYPE_STRING:
value = g_strdup(string);
break;
@@ -1603,18 +1392,15 @@ static gboolean parse_player_metadata(struct media_player *mp,
}

DBG("%s=%s", key, value);
- g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
+ g_hash_table_replace(track, g_strdup(key), value);
dbus_message_iter_next(&dict);
}

if (g_hash_table_size(track) == 0) {
g_hash_table_unref(track);
track = NULL;
- } else if (title == FALSE) {
- uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
-
- g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
- }
+ } else if (title == FALSE)
+ g_hash_table_insert(track, g_strdup("Title"), g_strdup(""));

if (mp->track != NULL)
g_hash_table_unref(mp->track);
@@ -1692,7 +1478,8 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
return NULL;
}

- mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);

adapter->players = g_slist_append(adapter->players, mp);

--
1.7.11.7


2012-10-29 14:41:44

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 2/7] audio: Export remote player as children object path of device

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

The object implements org.bluez.MediaPlayer similar to the target role.
---
Makefile.am | 1 +
audio/avrcp.c | 260 ++++++++++++++++++++++++++---------------
audio/player.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
audio/player.h | 37 ++++++
doc/media-api.txt | 20 +++-
5 files changed, 564 insertions(+), 97 deletions(-)
create mode 100644 audio/player.c
create mode 100644 audio/player.h

diff --git a/Makefile.am b/Makefile.am
index 35b1520..6ac6a73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,6 +148,7 @@ builtin_sources += audio/main.c \
audio/avdtp.h audio/avdtp.c \
audio/media.h audio/media.c \
audio/transport.h audio/transport.c \
+ audio/player.h audio/player.c \
audio/telephony.h audio/a2dp-codecs.h
builtin_nodist += audio/telephony.c

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 4f14513..7c26491 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -61,6 +61,7 @@
#include "control.h"
#include "avdtp.h"
#include "sink.h"
+#include "player.h"

/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -432,6 +433,36 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}

+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 player_get_setting(struct avrcp_player *player, uint8_t id)
{
if (player == NULL)
@@ -440,6 +471,24 @@ static int player_get_setting(struct avrcp_player *player, uint8_t id)
return player->cb->get_setting(id, player->user_data);
}

+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;
+}
+
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
@@ -463,7 +512,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
- pdu->params[1] = *((uint8_t *)data);
+ pdu->params[1] = play_status_to_val(data);

break;
case AVRCP_EVENT_TRACK_CHANGED:
@@ -480,9 +529,14 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
settings = data;
pdu->params[1] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ int attr;
int val;

+ attr = attr_to_val(key);
+ if (attr < 0)
+ continue;
+
val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -519,13 +573,35 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}

+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 const char *player_get_metadata(struct avrcp_player *player,
- uint32_t attr)
+ uint32_t id)
{
if (player != NULL)
- return player->cb->get_metadata(attr, player->user_data);
+ return player->cb->get_metadata(id, player->user_data);

- if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";

return NULL;
@@ -627,6 +703,49 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
return TRUE;
}

+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 player_set_setting(struct avrcp_player *player, uint8_t id,
uint8_t val)
{
@@ -1077,7 +1196,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,

pdu->params[++len] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ uint8_t attr = attr_to_val(key);
int val;

val = player_get_setting(player, attr);
@@ -1367,6 +1487,9 @@ static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
uint8_t *operands, size_t operand_count,
void *user_data)
{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
struct avrcp_header *pdu = (void *) operands;
uint32_t duration;
uint32_t position;
@@ -1380,9 +1503,10 @@ static gboolean avrcp_get_play_status_rsp(struct avctp *conn,

memcpy(&position, pdu->params + 4, sizeof(uint32_t));
position = ntohl(position);
+ media_player_set_position(mp, position);

memcpy(&status, pdu->params + 8, sizeof(uint8_t));
- DBG("%s", status_to_string(status));
+ media_player_set_status(mp, status_to_string(status));

return FALSE;
}
@@ -1404,70 +1528,14 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}

-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 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 gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
struct avrcp_header *pdu = (void *) operands;
uint8_t count;
int i;
@@ -1492,7 +1560,7 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
if (value == NULL)
continue;

- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}

return FALSE;
@@ -1563,33 +1631,14 @@ static void avrcp_list_player_attributes(struct avrcp *session)
session);
}

-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 gboolean avrcp_get_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
struct avrcp_header *pdu = (void *) operands;
uint8_t count;
int i;
@@ -1622,10 +1671,11 @@ static gboolean avrcp_get_attributes_rsp(struct avctp *conn,

if (charset == 106) {
const char *key = metadata_to_str(id);
- char *value = g_strndup((char *) &pdu->params[i], len);

- DBG("%s: %s", key , value);
- g_free(value);
+ if (key != NULL)
+ media_player_set_metadata(mp,
+ metadata_to_str(id),
+ &pdu->params[i], len);
}

i += len;
@@ -1663,9 +1713,11 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp *session = user_data;
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
+ struct media_player *mp;
uint8_t event;
uint8_t value;
uint8_t count;
+ const char *curval, *strval;
int i;

if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
@@ -1683,17 +1735,29 @@ static gboolean avrcp_handle_event(struct avctp *conn,

break;
case AVRCP_EVENT_STATUS_CHANGED:
+ mp = player->user_data;
value = pdu->params[1];

- avrcp_get_play_status(session);
+ curval = media_player_get_status(mp);
+ strval = status_to_string(value);
+
+ if (g_strcmp0(curval, strval) != 0) {
+ media_player_set_status(mp, strval);
+ avrcp_get_play_status(session);
+ }

break;
case AVRCP_EVENT_TRACK_CHANGED:
+ mp = player->user_data;
+ if (code == AVC_CTYPE_CHANGED)
+ media_player_set_position(mp, 0);
+
avrcp_get_element_attributes(session);

break;

case AVRCP_EVENT_SETTINGS_CHANGED:
+ mp = player->user_data;
count = pdu->params[1];

for (i = 2; count > 0; count--, i += 2) {
@@ -1709,7 +1773,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
if (value == NULL)
continue;

- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}

break;
@@ -1753,9 +1817,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
void *user_data)
{
struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp;
struct avrcp_header *pdu = (void *) operands;
uint16_t events = 0;
uint8_t count;
+ const char *path;

if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
return FALSE;
@@ -1776,6 +1843,11 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
}
}

+ path = device_get_path(session->dev->btd_dev);
+ mp = media_player_controller_create(path);
+ player->user_data = mp;
+ player->destroy = (GDestroyNotify) media_player_destroy;
+
if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
avrcp_list_player_attributes(session);

diff --git a/audio/player.c b/audio/player.c
new file mode 100644
index 0000000..1957594
--- /dev/null
+++ b/audio/player.c
@@ -0,0 +1,343 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "player.h"
+#include "dbus-common.h"
+#include "error.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+struct player_callback {
+ const struct media_player_callback *cbs;
+ void *user_data;
+};
+
+struct media_player {
+ char *path; /* Player object path */
+ GHashTable *settings; /* Player settings */
+ GHashTable *track; /* Player current track */
+ char *status;
+ uint32_t position;
+ GTimer *progress;
+ guint process_id;
+ struct player_callback *cb;
+};
+
+static void append_settings(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static void append_metadata(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ if (strcasecmp((char *) key, "Duration") == 0 ||
+ strcasecmp((char *) key, "Track") == 0 ||
+ strcasecmp((char *) key, "NumberOfTracks") == 0) {
+ uint32_t num = atoi(value);
+ dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+ return;
+ }
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static DBusMessage *media_player_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ uint32_t position;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ position = media_player_get_position(mp);
+ dict_append_entry(&dict, "Position", DBUS_TYPE_UINT32, &position);
+
+ dict_append_entry(&dict, "Status", DBUS_TYPE_STRING, &mp->status);
+
+ g_hash_table_foreach(mp->settings, append_settings, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_get_track(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ media_player_get_properties) },
+ { GDBUS_METHOD("GetTrack",
+ NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
+ media_player_get_track) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, media_player_set_property) },
+ { }
+};
+
+static const GDBusSignalTable media_player_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("TrackChanged",
+ GDBUS_ARGS({ "metadata", "a{sv}" })) },
+ { }
+};
+
+void media_player_destroy(struct media_player *mp)
+{
+ DBG("%s", mp->path);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
+ MEDIA_PLAYER_INTERFACE);
+
+ if (mp->track)
+ g_hash_table_unref(mp->track);
+
+ if (mp->settings)
+ g_hash_table_unref(mp->settings);
+
+ if (mp->process_id > 0)
+ g_source_remove(mp->process_id);
+
+ g_timer_destroy(mp->progress);
+ g_free(mp->cb);
+ g_free(mp->status);
+ g_free(mp->path);
+ g_free(mp);
+}
+
+struct media_player *media_player_controller_create(const char *path)
+{
+ struct media_player *mp;
+
+ mp = g_new0(struct media_player, 1);
+ mp->path = g_strdup_printf("%s/player1", path);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->progress = g_timer_new();
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ media_player_methods,
+ media_player_signals,
+ NULL, mp, NULL)) {
+ error("D-Bus failed to register %s path", mp->path);
+ media_player_destroy(mp);
+ return NULL;
+ }
+
+ DBG("%s", mp->path);
+
+ return mp;
+}
+
+uint32_t media_player_get_position(struct media_player *mp)
+{
+ double timedelta;
+ uint32_t sec, msec;
+
+ if (g_strcmp0(mp->status, "playing") != 0)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->progress, NULL);
+
+ sec = (uint32_t) timedelta;
+ msec = (uint32_t) ((timedelta - sec) * 1000);
+
+ return mp->position + sec * 1000 + msec;
+}
+
+void media_player_set_position(struct media_player *mp, uint32_t position)
+{
+ DBG("%u", position);
+
+ mp->position = position;
+ g_timer_start(mp->progress);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Position",
+ DBUS_TYPE_UINT32, &mp->position);
+}
+
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value)
+{
+ char *curval;
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return;
+
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
+ DBUS_TYPE_STRING, &value);
+}
+
+const char *media_player_get_status(struct media_player *mp)
+{
+ return mp->status;
+}
+
+void media_player_set_status(struct media_player *mp, const char *status)
+{
+ DBG("%s", status);
+
+ if (g_strcmp0(mp->status, status) == 0)
+ return;
+
+ g_free(mp->status);
+ mp->status = g_strdup(status);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Status",
+ DBUS_TYPE_STRING, &status);
+
+ mp->position = media_player_get_position(mp);
+ g_timer_start(mp->progress);
+}
+
+static gboolean process_metadata_changed(void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBusMessage *signal;
+ DBusMessageIter iter, dict;
+
+ mp->process_id = 0;
+
+ signal = dbus_message_new_signal(mp->path, MEDIA_PLAYER_INTERFACE,
+ "TrackChanged");
+ if (signal == NULL) {
+ error("Unable to allocate TrackChanged signal");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(btd_get_dbus_connection(), signal);
+
+ return FALSE;
+}
+
+void media_player_set_metadata(struct media_player *mp, const char *key,
+ void *data, size_t len)
+{
+ char *value, *curval;
+
+ value = g_strndup(data, len);
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->track, key);
+ if (g_strcmp0(curval, value) == 0) {
+ g_free(value);
+ return;
+ }
+
+ if (mp->process_id == 0) {
+ g_hash_table_remove_all(mp->track);
+ mp->process_id = g_idle_add(process_metadata_changed, mp);
+ }
+
+ g_hash_table_replace(mp->track, g_strdup(key), value);
+}
diff --git a/audio/player.h b/audio/player.h
new file mode 100644
index 0000000..f3a421a
--- /dev/null
+++ b/audio/player.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct media_player;
+
+struct media_player *media_player_controller_create(const char *path);
+void media_player_destroy(struct media_player *mp);
+uint32_t media_player_get_position(struct media_player *mp);
+void media_player_set_position(struct media_player *mp, uint32_t position);
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value);
+const char *media_player_get_status(struct media_player *mp);
+void media_player_set_status(struct media_player *mp, const char *status);
+void media_player_set_metadata(struct media_player *mp, const char *key,
+ void *data, size_t len);
diff --git a/doc/media-api.txt b/doc/media-api.txt
index d15d22a..b4f2fc6 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -126,11 +126,25 @@ Methods void RegisterEndpoint(object endpoint, dict properties)
MediaPlayer hierarchy
=====================

-Service unique name
+Service unique name (Target role)
+ org.bluez (Controller role)
Interface org.bluez.MediaPlayer
-Object path freely definable
+Object path freely definable (Target role)
+ [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+ (Controller role)
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ dict GetTrack()
+
+ Returns known metadata of the current track.
+
+ See TrackChanged for possible values.

-Methods void SetProperty(string property, variant value)
+ void SetProperty(string property, variant value)

Changes the value of the specified property. Only
properties that are listed as read-write can be changed.
--
1.7.11.7


2012-10-29 14:41:47

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 5/7] test: Fix using Number instead of Track in simple-player

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

---
test/simple-player | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/simple-player b/test/simple-player
index 70701da..f8751d6 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -111,7 +111,7 @@ if __name__ == '__main__':
"Album" : "Album",
"Genre" : "Genre",
"NumberOfTracks" : dbus.UInt32(10),
- "Number" : dbus.UInt32(1),
+ "Track" : dbus.UInt32(1),
"Duration" : dbus.UInt32(10000) }, signature="sv")

print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
--
1.7.11.7


2012-10-29 14:41:45

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 3/7] player: Add support for SetProperty

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

Properties Equalizer, Repeat, Shuffle and Scan can be set by user
application.
---
audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
audio/player.h | 9 ++++
3 files changed, 288 insertions(+), 4 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 7c26491..724e139 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
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 int attr_to_val(const char *str)
{
if (!strcasecmp(str, "Equalizer"))
@@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}

+static const char *status_to_str(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_STATUS_INVALID_COMMAND:
+ return "Invalid Command";
+ case AVRCP_STATUS_INVALID_PARAM:
+ return "Invalid Parameter";
+ case AVRCP_STATUS_INTERNAL_ERROR:
+ return "Internal Error";
+ case AVRCP_STATUS_SUCCESS:
+ return "Success";
+ default:
+ return "Unknown";
+ }
+}
+
static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t count;
int i;

- if (code == AVC_CTYPE_REJECTED)
+ if (code == AVC_CTYPE_REJECTED) {
+ media_player_set_setting(mp, "Error",
+ status_to_str(pdu->params[0]));
return FALSE;
+ }

count = pdu->params[0];

@@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}

+static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
+ uint8_t val)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 3];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = 1;
+ pdu->params[1] = attr;
+ pdu->params[2] = val;
+ pdu->params_len = htons(3);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_player_value_rsp, session);
+}
+
+static bool ct_set_setting(struct media_player *mp, const char *key,
+ const char *value, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ int attr = attr_to_val(key);
+ int val = attrval_to_val(attr, value);
+ struct avrcp *session;
+
+ session = player->sessions->data;
+ if (session == NULL)
+ return false;
+
+ attr = attr_to_val(key);
+ if (attr < 0)
+ return false;
+
+ val = attrval_to_val(attr, value);
+ if (val < 0)
+ return false;
+
+ avrcp_set_player_value(session, attr, val);
+
+ return true;
+}
+
+static const struct media_player_callback ct_cbs = {
+ .set_setting = ct_set_setting,
+};
+
static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,

path = device_get_path(session->dev->btd_dev);
mp = media_player_controller_create(path);
+ media_player_set_callbacks(mp, &ct_cbs, player);
player->user_data = mp;
player->destroy = (GDestroyNotify) media_player_destroy;

diff --git a/audio/player.c b/audio/player.c
index 1957594..e83b761 100644
--- a/audio/player.c
+++ b/audio/player.c
@@ -49,6 +49,12 @@ struct player_callback {
void *user_data;
};

+struct pending_req {
+ DBusMessage *msg;
+ const char *key;
+ const char *value;
+};
+
struct media_player {
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
@@ -58,6 +64,7 @@ struct media_player {
GTimer *progress;
guint process_id;
struct player_callback *cb;
+ GSList *pending;
};

static void append_settings(void *key, void *value, void *user_data)
@@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
return reply;
}

+static struct pending_req *find_pending(struct media_player *mp,
+ const char *key)
+{
+ GSList *l;
+
+ for (l = mp->pending; l; l = l->next) {
+ struct pending_req *p = l->data;
+
+ if (strcasecmp(key, p->key) == 0)
+ return p;
+ }
+
+ return NULL;
+}
+
+static struct pending_req *pending_new(DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct pending_req *p;
+
+ p = g_new0(struct pending_req, 1);
+ p->msg = dbus_message_ref(msg);
+ p->key = key;
+ p->value = value;
+
+ return p;
+}
+
+static DBusMessage *player_set_setting(struct media_player *mp,
+ DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct player_callback *cb = mp->cb;
+ struct pending_req *p;
+
+ if (cb == NULL || cb->cbs->set_setting == NULL)
+ return btd_error_not_supported(msg);
+
+ p = find_pending(mp, key);
+ if (p != NULL)
+ return btd_error_in_progress(msg);
+
+ if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
+ return btd_error_invalid_args(msg);
+
+ p = pending_new(msg, key, value);
+
+ mp->pending = g_slist_append(mp->pending, p);
+
+ return NULL;
+}
+
static DBusMessage *media_player_set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ struct media_player *mp = data;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *key, *value, *curval;
+
+ 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, &key);
+ 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);
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (g_strcmp0(key, "Equalizer") != 0 &&
+ g_strcmp0(key, "Repeat") != 0 &&
+ g_strcmp0(key, "Shuffle") != 0 &&
+ g_strcmp0(key, "Scan") != 0)
+ return btd_error_invalid_args(msg);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ return player_set_setting(mp, msg, key, value);
}

static const GDBusMethodTable media_player_methods[] = {
@@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("GetTrack",
NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
media_player_get_track) },
- { GDBUS_METHOD("SetProperty",
+ { GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
NULL, media_player_set_property) },
{ }
@@ -169,6 +262,14 @@ static const GDBusSignalTable media_player_signals[] = {
{ }
};

+static void pending_free(void *data)
+{
+ struct pending_req *p = data;
+
+ dbus_message_unref(p->msg);
+ g_free(p);
+}
+
void media_player_destroy(struct media_player *mp)
{
DBG("%s", mp->path);
@@ -185,6 +286,8 @@ void media_player_destroy(struct media_player *mp)
if (mp->process_id > 0)
g_source_remove(mp->process_id);

+ g_slist_free_full(mp->pending, pending_free);
+
g_timer_destroy(mp->progress);
g_free(mp->cb);
g_free(mp->status);
@@ -250,17 +353,46 @@ void media_player_set_setting(struct media_player *mp, const char *key,
const char *value)
{
char *curval;
+ struct pending_req *p;
+ DBusMessage *reply;

DBG("%s: %s", key, value);

+ if (strcasecmp(key, "Error") == 0) {
+ p = g_slist_nth_data(mp->pending, 0);
+ if (p == NULL)
+ return;
+
+ reply = btd_error_failed(p->msg, value);
+ goto send;
+ }
+
curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
- return;
+ goto done;

g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));

emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
DBUS_TYPE_STRING, &value);
+
+done:
+ p = find_pending(mp, key);
+ if (p == NULL)
+ return;
+
+ if (strcasecmp(value, p->value) == 0)
+ reply = g_dbus_create_reply(p->msg, DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_not_supported(p->msg);
+
+send:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ mp->pending = g_slist_remove(mp->pending, p);
+ pending_free(p);
+
+ return;
}

const char *media_player_get_status(struct media_player *mp)
@@ -341,3 +473,19 @@ void media_player_set_metadata(struct media_player *mp, const char *key,

g_hash_table_replace(mp->track, g_strdup(key), value);
}
+
+void media_player_set_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data)
+{
+ struct player_callback *cb;
+
+ if (mp->cb)
+ g_free(mp->cb);
+
+ cb = g_new0(struct player_callback, 1);
+ cb->cbs = cbs;
+ cb->user_data = user_data;
+
+ mp->cb = cb;
+}
diff --git a/audio/player.h b/audio/player.h
index f3a421a..4a6a9cc 100644
--- a/audio/player.h
+++ b/audio/player.h
@@ -25,6 +25,11 @@

struct media_player;

+struct media_player_callback {
+ bool (*set_setting) (struct media_player *mp, const char *key,
+ const char *value, void *user_data);
+};
+
struct media_player *media_player_controller_create(const char *path);
void media_player_destroy(struct media_player *mp);
uint32_t media_player_get_position(struct media_player *mp);
@@ -35,3 +40,7 @@ const char *media_player_get_status(struct media_player *mp);
void media_player_set_status(struct media_player *mp, const char *status);
void media_player_set_metadata(struct media_player *mp, const char *key,
void *data, size_t len);
+
+void media_player_set_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data);
--
1.7.11.7


2012-10-29 14:41:48

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 6/7] test: Fix using Number instead of Track in mpris-player

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

---
test/mpris-player.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/mpris-player.c b/test/mpris-player.c
index a2c4cc6..13f5c85 100644
--- a/test/mpris-player.c
+++ b/test/mpris-player.c
@@ -338,7 +338,7 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,

dbus_message_iter_get_basic(&var, &value);

- dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+ dict_append_entry(metadata, "Track", DBUS_TYPE_UINT32,
&value);
}

--
1.7.11.7


2012-10-29 14:41:49

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 7/7] test: Add support for using external player

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

This add support for passing a device player object which is then
registered as a local one in a loopback fashion.
---
test/simple-player | 80 +++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 61 insertions(+), 19 deletions(-)

diff --git a/test/simple-player b/test/simple-player
index f8751d6..9e72f3e 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -9,6 +9,50 @@ import dbus.mainloop.glib
import gobject

class Player(dbus.service.Object):
+ properties = None
+ metadata = None
+
+ def set_object(self, obj = None):
+ if obj != None:
+ bus = dbus.SystemBus()
+ mp = dbus.Interface(bus.get_object("org.bluez", obj),
+ "org.bluez.MediaPlayer")
+
+ self.properties = mp.GetProperties()
+ self.metadata = mp.GetTrack()
+
+ bus.add_signal_receiver(self.property_changed,
+ path = obj,
+ dbus_interface = "org.bluez.MediaPlayer",
+ signal_name = "PropertyChanged")
+
+ bus.add_signal_receiver(self.track_changed,
+ path = obj,
+ dbus_interface ="org.bluez.MediaPlayer",
+ signal_name = "TrackChanged")
+ else:
+ self.properties = dbus.Dictionary({
+ "Equalizer" : "off",
+ "Repeat" : "off",
+ "Shuffle" : "off",
+ "Scan" : "off",
+ "Status" : "playing",
+ "Position" : dbus.UInt32(0) },
+ signature="sv")
+
+ self.metadata = dbus.Dictionary({
+ "Title" : "Title",
+ "Artist" : "Artist",
+ "Album" : "Album",
+ "Genre" : "Genre",
+ "NumberOfTracks" : dbus.UInt32(10),
+ "Track" : dbus.UInt32(1),
+ "Duration" : dbus.UInt32(10000) },
+ signature="sv")
+ handler = InputHandler(self)
+ gobject.io_add_watch(sys.stdin, gobject.IO_IN,
+ handler.handle)
+
@dbus.service.method("org.bluez.MediaPlayer",
in_signature="", out_signature="")
def Release(self):
@@ -42,6 +86,16 @@ class Player(dbus.service.Object):
def help(self, func):
help(self.__class__.__dict__[func])

+ def property_changed(self, setting, value):
+ print("property_changed(%s, %s)" % (setting, value))
+
+ self.PropertyChanged(setting, value)
+
+ def track_changed(self, metadata):
+ print("track_changed(%s)" % (metadata))
+
+ self.TrackChanged(metadata)
+
class InputHandler:
commands = { 'TrackChanged': '(metadata)',
'PropertyChanged': '(key, value)',
@@ -79,7 +133,6 @@ class InputHandler:
return True


-
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

@@ -99,27 +152,16 @@ if __name__ == '__main__':
player = Player(bus, path)
mainloop = gobject.MainLoop()

- properties = dbus.Dictionary({ "Equalizer" : "off",
- "Repeat" : "off",
- "Shuffle" : "off",
- "Scan" : "off",
- "Status" : "playing",
- "Position" : dbus.UInt32(0) }, signature="sv")
-
- metadata = dbus.Dictionary({ "Title" : "Title",
- "Artist" : "Artist",
- "Album" : "Album",
- "Genre" : "Genre",
- "NumberOfTracks" : dbus.UInt32(10),
- "Track" : dbus.UInt32(1),
- "Duration" : dbus.UInt32(10000) }, signature="sv")
+ if len(sys.argv) > 2:
+ player.set_object(sys.argv[2])
+ else:
+ player.set_object()

print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
- % (properties, metadata))
+ % (player.properties, player.metadata))

- handler = InputHandler(player)
- gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)

- media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+ media.RegisterPlayer(dbus.ObjectPath(path), player.properties,
+ player.metadata)

mainloop.run()
--
1.7.11.7