diff options
author | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2011-10-05 23:02:15 +0300 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2011-10-06 11:13:28 +0300 |
commit | 14ae307458e0bf1a3014bc799919244318d09293 (patch) | |
tree | 26e99652df1ba599b252d426b8c5d0c837740ff7 /audio/avrcp.c | |
parent | e2746b6fcc76c0a90ebffcfa6e61cf89aa538ab6 (diff) | |
download | bluez-14ae307458e0bf1a3014bc799919244318d09293.tar.gz |
AVRCP: move MediaPlayer to adapter object
This move the MediaPlayer registration to adapter object on Media
interface so we can track players properly.
Diffstat (limited to 'audio/avrcp.c')
-rw-r--r-- | audio/avrcp.c | 840 |
1 files changed, 168 insertions, 672 deletions
diff --git a/audio/avrcp.c b/audio/avrcp.c index 6e8b8ff23..96a0d363b 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) { 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) { 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); } |