Return-Path: MIME-Version: 1.0 In-Reply-To: <1317844938-21950-1-git-send-email-luiz.dentz@gmail.com> References: <1317844938-21950-1-git-send-email-luiz.dentz@gmail.com> From: Lucas De Marchi Date: Wed, 5 Oct 2011 19:48:47 -0300 Message-ID: Subject: Re: [PATCH BlueZ 1/4] AVRCP: move MediaPlayer to adapter object To: Luiz Augusto von Dentz Cc: linux-bluetooth@vger.kernel.org Content-Type: text/plain; charset=ISO-8859-1 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi Luiz, On Wed, Oct 5, 2011 at 5:02 PM, Luiz Augusto von Dentz wrote: > > From: Luiz Augusto von Dentz > > This move the MediaPlayer registration to adapter object on Media > interface so we can track players properly. > --- > ?audio/avrcp.c ? ? | ?840 +++++++++++------------------------------------------ > ?audio/avrcp.h ? ? | ? 67 ++++- > ?audio/device.c ? ?| ? ?3 - > ?audio/device.h ? ?| ? ?2 - > ?audio/manager.c ? | ? ?7 - > ?audio/media.c ? ? | ?784 +++++++++++++++++++++++++++++++++++++++++++++++++ > ?doc/media-api.txt | ?163 +++++++++++ > ?7 files changed, 1179 insertions(+), 687 deletions(-) I did an interdiff from your previous patch. I'm commenting only on these changes. The others I think I had already reviewed all. > > diff --git a/audio/avrcp.c b/audio/avrcp.c > index 6e8b8ff..96a0d36 100644 > --- a/audio/avrcp.c > +++ b/audio/avrcp.c > @@ -78,54 +78,10 @@ > ?#define AVRCP_GET_PLAY_STATUS ? ? ? ? ?0x30 > ?#define AVRCP_REGISTER_NOTIFICATION ? ?0x31 > > -/* Notification events */ > -#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED ? ? ? ? ? ?0x01 > -#define AVRCP_EVENT_TRACK_CHANGED ? ? ? ? ? ? ? ? ? ? ?0x02 > - > ?/* Capabilities for AVRCP_GET_CAPABILITIES pdu */ > ?#define CAP_COMPANY_ID ? ? ? ? 0x02 > ?#define CAP_EVENTS_SUPPORTED ? 0x03 > > -enum player_setting { > - ? ? ? PLAYER_SETTING_EQUALIZER = ? ? ?1, > - ? ? ? PLAYER_SETTING_REPEAT = ? ? ? ? 2, > - ? ? ? PLAYER_SETTING_SHUFFLE = ? ? ? ?3, > - ? ? ? PLAYER_SETTING_SCAN = ? ? ? ? ? 4, > -}; > - > -enum equalizer_mode { > - ? ? ? EQUALIZER_MODE_OFF = ? ?1, > - ? ? ? EQUALIZER_MODE_ON = ? ? 2, > -}; > - > -enum repeat_mode { > - ? ? ? REPEAT_MODE_OFF = ? ? ? 1, > - ? ? ? REPEAT_MODE_SINGLE = ? ?2, > - ? ? ? REPEAT_MODE_ALL = ? ? ? 3, > - ? ? ? REPEAT_MODE_GROUP = ? ? 4, > -}; > - > -enum shuffle_mode { > - ? ? ? SHUFFLE_MODE_OFF = ? ? ?1, > - ? ? ? SHUFFLE_MODE_ALL = ? ? ?2, > - ? ? ? SHUFFLE_MODE_GROUP = ? ?3, > -}; > - > -enum scan_mode { > - ? ? ? SCAN_MODE_OFF = ? ? ? ? 1, > - ? ? ? SCAN_MODE_ALL = ? ? ? ? 2, > - ? ? ? SCAN_MODE_GROUP = ? ? ? 3, > -}; > - > -enum play_status { > - ? ? ? PLAY_STATUS_STOPPED = ? ? ? ? ? 0x00, > - ? ? ? PLAY_STATUS_PLAYING = ? ? ? ? ? 0x01, > - ? ? ? PLAY_STATUS_PAUSED = ? ? ? ? ? ?0x02, > - ? ? ? PLAY_STATUS_FWD_SEEK = ? ? ? ? ?0x03, > - ? ? ? PLAY_STATUS_REV_SEEK = ? ? ? ? ?0x04, > - ? ? ? PLAY_STATUS_ERROR = ? ? ? ? ? ? 0xFF > -}; > - > ?enum battery_status { > ? ? ? ?BATTERY_STATUS_NORMAL = ? ? ? ? 0, > ? ? ? ?BATTERY_STATUS_WARNING = ? ? ? ?1, > @@ -134,17 +90,6 @@ enum battery_status { > ? ? ? ?BATTERY_STATUS_FULL_CHARGE = ? ?4, > ?}; > > -enum media_info_id { > - ? ? ? MEDIA_INFO_TITLE = ? ? ? ? ? ? ?1, > - ? ? ? MEDIA_INFO_ARTIST = ? ? ? ? ? ? 2, > - ? ? ? MEDIA_INFO_ALBUM = ? ? ? ? ? ? ?3, > - ? ? ? MEDIA_INFO_TRACK = ? ? ? ? ? ? ?4, > - ? ? ? MEDIA_INFO_N_TRACKS = ? ? ? ? ? 5, > - ? ? ? MEDIA_INFO_GENRE = ? ? ? ? ? ? ?6, > - ? ? ? MEDIA_INFO_PLAYING_TIME = ? ? ? 7, > - ? ? ? MEDIA_INFO_LAST > -}; > - > ?#if __BYTE_ORDER == __LITTLE_ENDIAN > > ?struct avrcp_header { > @@ -180,33 +125,26 @@ struct avrcp_server { > ? ? ? ?bdaddr_t src; > ? ? ? ?uint32_t tg_record_id; > ? ? ? ?uint32_t ct_record_id; > + ? ? ? GSList *players; > + ? ? ? struct avrcp_player *active_player; > ?}; > > -struct media_info { > - ? ? ? char *title; > - ? ? ? char *artist; > - ? ? ? char *album; > - ? ? ? char *genre; > - ? ? ? uint32_t ntracks; > - ? ? ? uint32_t track; > - ? ? ? uint32_t track_len; > - ? ? ? uint32_t elapsed; > -}; > - > -struct media_player { > +struct avrcp_player { > + ? ? ? struct avrcp_server *server; > ? ? ? ?struct avctp *session; > ? ? ? ?struct audio_device *dev; > - ? ? ? uint8_t settings[PLAYER_SETTING_SCAN + 1]; > - ? ? ? enum play_status status; > > - ? ? ? struct media_info mi; > - ? ? ? GTimer *timer; > ? ? ? ?unsigned int handler; > ? ? ? ?uint16_t registered_events; > ? ? ? ?uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1]; > + > + ? ? ? struct avrcp_player_cb *cb; > + ? ? ? void *user_data; > + ? ? ? GDestroyNotify destroy; > ?}; > > ?static GSList *servers = NULL; > +static unsigned int avctp_id = 0; > > ?/* Company IDs supported by this device */ > ?static uint32_t company_ids[] = { > @@ -344,164 +282,19 @@ static sdp_record_t *avrcp_tg_record(void) > ?static unsigned int attr_get_max_val(uint8_t attr) > ?{ > ? ? ? ?switch (attr) { > - ? ? ? case PLAYER_SETTING_EQUALIZER: > - ? ? ? ? ? ? ? return EQUALIZER_MODE_ON; > - ? ? ? case PLAYER_SETTING_REPEAT: > - ? ? ? ? ? ? ? return REPEAT_MODE_GROUP; > - ? ? ? case PLAYER_SETTING_SHUFFLE: > - ? ? ? ? ? ? ? return SHUFFLE_MODE_GROUP; > - ? ? ? case PLAYER_SETTING_SCAN: > - ? ? ? ? ? ? ? return SCAN_MODE_GROUP; > + ? ? ? case AVRCP_ATTRIBUTE_EQUALIZER: > + ? ? ? ? ? ? ? return AVRCP_EQUALIZER_ON; > + ? ? ? case AVRCP_ATTRIBUTE_REPEAT_MODE: > + ? ? ? ? ? ? ? return AVRCP_REPEAT_MODE_GROUP; > + ? ? ? case AVRCP_ATTRIBUTE_SHUFFLE: > + ? ? ? ? ? ? ? return AVRCP_SHUFFLE_GROUP; > + ? ? ? case AVRCP_ATTRIBUTE_SCAN: > + ? ? ? ? ? ? ? return AVRCP_SCAN_GROUP; > ? ? ? ?} > > ? ? ? ?return 0; > ?} > > -static const char *attrval_to_str(uint8_t attr, uint8_t value) > -{ > - ? ? ? switch (attr) { > - ? ? ? case PLAYER_SETTING_EQUALIZER: > - ? ? ? ? ? ? ? switch (value) { > - ? ? ? ? ? ? ? case EQUALIZER_MODE_ON: > - ? ? ? ? ? ? ? ? ? ? ? return "on"; > - ? ? ? ? ? ? ? case EQUALIZER_MODE_OFF: > - ? ? ? ? ? ? ? ? ? ? ? return "off"; > - ? ? ? ? ? ? ? } > - > - ? ? ? ? ? ? ? break; > - ? ? ? case PLAYER_SETTING_REPEAT: > - ? ? ? ? ? ? ? switch (value) { > - ? ? ? ? ? ? ? case REPEAT_MODE_OFF: > - ? ? ? ? ? ? ? ? ? ? ? return "off"; > - ? ? ? ? ? ? ? case REPEAT_MODE_SINGLE: > - ? ? ? ? ? ? ? ? ? ? ? return "singletrack"; > - ? ? ? ? ? ? ? case REPEAT_MODE_ALL: > - ? ? ? ? ? ? ? ? ? ? ? return "alltracks"; > - ? ? ? ? ? ? ? case REPEAT_MODE_GROUP: > - ? ? ? ? ? ? ? ? ? ? ? return "group"; > - ? ? ? ? ? ? ? } > - > - ? ? ? ? ? ? ? break; > - ? ? ? /* Shuffle and scan have the same values */ > - ? ? ? case PLAYER_SETTING_SHUFFLE: > - ? ? ? case PLAYER_SETTING_SCAN: > - ? ? ? ? ? ? ? switch (value) { > - ? ? ? ? ? ? ? case SCAN_MODE_OFF: > - ? ? ? ? ? ? ? ? ? ? ? return "off"; > - ? ? ? ? ? ? ? case SCAN_MODE_ALL: > - ? ? ? ? ? ? ? ? ? ? ? return "alltracks"; > - ? ? ? ? ? ? ? case SCAN_MODE_GROUP: > - ? ? ? ? ? ? ? ? ? ? ? return "group"; > - ? ? ? ? ? ? ? } > - > - ? ? ? ? ? ? ? break; > - ? ? ? } > - > - ? ? ? return NULL; > -} > - > -static int attrval_to_val(uint8_t attr, const char *value) > -{ > - ? ? ? int ret; > - > - ? ? ? switch (attr) { > - ? ? ? case PLAYER_SETTING_EQUALIZER: > - ? ? ? ? ? ? ? if (!strcmp(value, "off")) > - ? ? ? ? ? ? ? ? ? ? ? ret = EQUALIZER_MODE_OFF; > - ? ? ? ? ? ? ? else if (!strcmp(value, "on")) > - ? ? ? ? ? ? ? ? ? ? ? ret = EQUALIZER_MODE_ON; > - ? ? ? ? ? ? ? else > - ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > - > - ? ? ? ? ? ? ? return ret; > - ? ? ? case PLAYER_SETTING_REPEAT: > - ? ? ? ? ? ? ? if (!strcmp(value, "off")) > - ? ? ? ? ? ? ? ? ? ? ? ret = REPEAT_MODE_OFF; > - ? ? ? ? ? ? ? else if (!strcmp(value, "singletrack")) > - ? ? ? ? ? ? ? ? ? ? ? ret = REPEAT_MODE_SINGLE; > - ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > - ? ? ? ? ? ? ? ? ? ? ? ret = REPEAT_MODE_ALL; > - ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > - ? ? ? ? ? ? ? ? ? ? ? ret = REPEAT_MODE_GROUP; > - ? ? ? ? ? ? ? else > - ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > - > - ? ? ? ? ? ? ? return ret; > - ? ? ? case PLAYER_SETTING_SHUFFLE: > - ? ? ? ? ? ? ? if (!strcmp(value, "off")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SHUFFLE_MODE_OFF; > - ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SHUFFLE_MODE_ALL; > - ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SHUFFLE_MODE_GROUP; > - ? ? ? ? ? ? ? else > - ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > - > - ? ? ? ? ? ? ? return ret; > - ? ? ? case PLAYER_SETTING_SCAN: > - ? ? ? ? ? ? ? if (!strcmp(value, "off")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SCAN_MODE_OFF; > - ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SCAN_MODE_ALL; > - ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > - ? ? ? ? ? ? ? ? ? ? ? ret = SCAN_MODE_GROUP; > - ? ? ? ? ? ? ? else > - ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > - > - ? ? ? ? ? ? ? return ret; > - ? ? ? } > - > - ? ? ? return -EINVAL; > -} > - > -static const char *attr_to_str(uint8_t attr) > -{ > - ? ? ? switch (attr) { > - ? ? ? case PLAYER_SETTING_EQUALIZER: > - ? ? ? ? ? ? ? return "Equalizer"; > - ? ? ? case PLAYER_SETTING_REPEAT: > - ? ? ? ? ? ? ? return "Repeat"; > - ? ? ? case PLAYER_SETTING_SHUFFLE: > - ? ? ? ? ? ? ? return "Shuffle"; > - ? ? ? case PLAYER_SETTING_SCAN: > - ? ? ? ? ? ? ? return "Scan"; > - ? ? ? } > - > - ? ? ? return NULL; > -} > - > -static int attr_to_val(const char *str) > -{ > - ? ? ? if (!strcmp(str, "Equalizer")) > - ? ? ? ? ? ? ? return PLAYER_SETTING_EQUALIZER; > - ? ? ? else if (!strcmp(str, "Repeat")) > - ? ? ? ? ? ? ? return PLAYER_SETTING_REPEAT; > - ? ? ? else if (!strcmp(str, "Shuffle")) > - ? ? ? ? ? ? ? return PLAYER_SETTING_SHUFFLE; > - ? ? ? else if (!strcmp(str, "Scan")) > - ? ? ? ? ? ? ? return PLAYER_SETTING_SCAN; > - > - ? ? ? return -EINVAL; > -} > - > -static int play_status_to_val(const char *status) > -{ > - ? ? ? if (!strcmp(status, "stopped")) > - ? ? ? ? ? ? ? return PLAY_STATUS_STOPPED; > - ? ? ? else if (!strcmp(status, "playing")) > - ? ? ? ? ? ? ? return PLAY_STATUS_PLAYING; > - ? ? ? else if (!strcmp(status, "paused")) > - ? ? ? ? ? ? ? return PLAY_STATUS_PAUSED; > - ? ? ? else if (!strcmp(status, "forward-seek")) > - ? ? ? ? ? ? ? return PLAY_STATUS_FWD_SEEK; > - ? ? ? else if (!strcmp(status, "reverse-seek")) > - ? ? ? ? ? ? ? return PLAY_STATUS_REV_SEEK; > - ? ? ? else if (!strcmp(status, "error")) > - ? ? ? ? ? ? ? return PLAY_STATUS_ERROR; > - > - ? ? ? return -EINVAL; > -} > - > ?static const char *battery_status_to_str(enum battery_status status) > ?{ > ? ? ? ?switch (status) { > @@ -542,17 +335,17 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in) > ? ? ? ?cid[2] = cid_in; > ?} > > -static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data) > +int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data) > ?{ > ? ? ? ?uint8_t buf[AVRCP_HEADER_LENGTH + 9]; > ? ? ? ?struct avrcp_header *pdu = (void *) buf; > ? ? ? ?uint16_t size; > ? ? ? ?int err; > > - ? ? ? if (mp->session == NULL) > + ? ? ? if (player->session == NULL) > ? ? ? ? ? ? ? ?return -ENOTCONN; > > - ? ? ? if (!(mp->registered_events & (1 << id))) > + ? ? ? if (!(player->registered_events & (1 << id))) > ? ? ? ? ? ? ? ?return 0; > > ? ? ? ?memset(buf, 0, sizeof(buf)); > @@ -565,7 +358,7 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data) > ? ? ? ?DBG("id=%u", id); > > ? ? ? ?switch (id) { > - ? ? ? case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED: > + ? ? ? case AVRCP_EVENT_STATUS_CHANGED: > ? ? ? ? ? ? ? ?size = 2; > ? ? ? ? ? ? ? ?pdu->params[1] = *((uint8_t *)data); > > @@ -589,58 +382,18 @@ static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data) > > ? ? ? ?pdu->params_len = htons(size); > > - ? ? ? err = avctp_send_vendordep(mp->session, mp->transaction_events[id], > + ? ? ? err = avctp_send_vendordep(player->session, player->transaction_events[id], > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?buf, size + AVRCP_HEADER_LENGTH); > ? ? ? ?if (err < 0) > ? ? ? ? ? ? ? ?return err; > > ? ? ? ?/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */ > - ? ? ? mp->registered_events ^= 1 << id; > + ? ? ? player->registered_events ^= 1 << id; > > ? ? ? ?return 0; > ?} > > -static void mp_get_playback_status(struct media_player *mp, uint8_t *status, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32_t *elapsed, uint32_t *track_len) > -{ > - ? ? ? if (status) > - ? ? ? ? ? ? ? *status = mp->status; > - ? ? ? if (track_len) > - ? ? ? ? ? ? ? *track_len = mp->mi.track_len; > - > - ? ? ? if (!elapsed) > - ? ? ? ? ? ? ? return; > - > - ? ? ? *elapsed = mp->mi.elapsed; > - > - ? ? ? if (mp->status == PLAY_STATUS_PLAYING) { > - ? ? ? ? ? ? ? double timedelta = g_timer_elapsed(mp->timer, NULL); > - ? ? ? ? ? ? ? uint32_t sec, msec; > - > - ? ? ? ? ? ? ? sec = (uint32_t) timedelta; > - ? ? ? ? ? ? ? msec = (uint32_t)((timedelta - sec) * 1000); > - > - ? ? ? ? ? ? ? *elapsed += sec * 1000 + msec; > - ? ? ? } > -} > - > -static void mp_set_playback_status(struct media_player *mp, uint8_t status, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32_t elapsed) > -{ > - ? ? ? DBG("Change playback: %u %u", status, elapsed); > - > - ? ? ? mp->mi.elapsed = elapsed; > - ? ? ? g_timer_start(mp->timer); > - > - ? ? ? if (status == mp->status) > - ? ? ? ? ? ? ? return; > - > - ? ? ? mp->status = status; > - > - ? ? ? avrcp_send_event(mp, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED, &status); > -} > - > ?/* > ?* Copy media_info field to a buffer, intended to be used in a response to > ?* GetElementAttributes message. > @@ -648,10 +401,10 @@ static void mp_set_playback_status(struct media_player *mp, uint8_t status, > ?* It assumes there's enough space in the buffer and on success it returns the > ?* size written. > ?* > - * If @param id is not valid, -EINVAL is returned. If there's no such media > - * attribute, -ENOENT is returned. > + * If @param id is not valid, -EINVAL is returned. If there's no space left on > + * the buffer -ENOBUFS is returned. > ?*/ > -static int mp_get_media_attribute(struct media_player *mp, > +static int player_get_media_attribute(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint32_t id, uint8_t *buf, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint16_t maxlen) > ?{ > @@ -661,11 +414,10 @@ static int mp_get_media_attribute(struct media_player *mp, > ? ? ? ? ? ? ? ?uint16_t len; > ? ? ? ? ? ? ? ?uint8_t val[]; > ? ? ? ?}; > - ? ? ? const struct media_info *mi = &mp->mi; > ? ? ? ?struct media_info_elem *elem = (void *)buf; > ? ? ? ?uint16_t len; > ? ? ? ?char valstr[20]; > - ? ? ? char *valp; > + ? ? ? void *value; > > ? ? ? ?if (maxlen < sizeof(struct media_info_elem)) > ? ? ? ? ? ? ? ?return -ENOBUFS; > @@ -673,61 +425,38 @@ static int mp_get_media_attribute(struct media_player *mp, > ? ? ? ?/* Subtract the size of elem header from the available space */ > ? ? ? ?maxlen -= sizeof(struct media_info_elem); > > - ? ? ? switch (id) { > - ? ? ? case MEDIA_INFO_TITLE: > - ? ? ? ? ? ? ? valp = mi->title; > - ? ? ? ? ? ? ? break; > - ? ? ? case MEDIA_INFO_ARTIST: > - ? ? ? ? ? ? ? valp = mi->artist; > - ? ? ? ? ? ? ? break; > - ? ? ? case MEDIA_INFO_ALBUM: > - ? ? ? ? ? ? ? valp = mi->album; > - ? ? ? ? ? ? ? break; > - ? ? ? case MEDIA_INFO_GENRE: > - ? ? ? ? ? ? ? valp = mi->genre; > - ? ? ? ? ? ? ? break; > - ? ? ? case MEDIA_INFO_TRACK: > - ? ? ? ? ? ? ? if (mi->track) { > - ? ? ? ? ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->track); > - ? ? ? ? ? ? ? ? ? ? ? valp = valstr; > - ? ? ? ? ? ? ? } else { > - ? ? ? ? ? ? ? ? ? ? ? valp = NULL; > - ? ? ? ? ? ? ? } > + ? ? ? DBG("Get media attribute: %u", id); > > - ? ? ? ? ? ? ? break; > - ? ? ? case MEDIA_INFO_N_TRACKS: > - ? ? ? ? ? ? ? if (mi->ntracks) { > - ? ? ? ? ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->ntracks); > - ? ? ? ? ? ? ? ? ? ? ? valp = valstr; > - ? ? ? ? ? ? ? } else { > - ? ? ? ? ? ? ? ? ? ? ? valp = NULL; > - ? ? ? ? ? ? ? } > + ? ? ? value = player->cb->get_metadata(id, player->user_data); > + ? ? ? if (value == NULL) { > + ? ? ? ? ? ? ? len = 0; > + ? ? ? ? ? ? ? goto done; > + ? ? ? } > > + ? ? ? switch (id) { > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TITLE: > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ARTIST: > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ALBUM: > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_GENRE: > + ? ? ? ? ? ? ? len = strlen((char *) value); > + ? ? ? ? ? ? ? if (len > maxlen) > + ? ? ? ? ? ? ? ? ? ? ? return -ENOBUFS; > + ? ? ? ? ? ? ? memcpy(elem->val, value, len); > ? ? ? ? ? ? ? ?break; > - ? ? ? case MEDIA_INFO_PLAYING_TIME: > - ? ? ? ? ? ? ? if (mi->track_len == 0xFFFFFFFF) { > - ? ? ? ? ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", mi->track_len); > - ? ? ? ? ? ? ? ? ? ? ? valp = valstr; > - ? ? ? ? ? ? ? } else { > - ? ? ? ? ? ? ? ? ? ? ? valp = NULL; > - ? ? ? ? ? ? ? } > - > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TRACK: > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_DURATION: > + ? ? ? ? ? ? ? snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value)); > + ? ? ? ? ? ? ? len = strlen(valstr); > + ? ? ? ? ? ? ? if (len > maxlen) > + ? ? ? ? ? ? ? ? ? ? ? return -ENOBUFS; > + ? ? ? ? ? ? ? memcpy(elem->val, valstr, len); > ? ? ? ? ? ? ? ?break; > ? ? ? ?default: > ? ? ? ? ? ? ? ?return -ENOENT; > ? ? ? ?} > > - ? ? ? if (valp) { > - ? ? ? ? ? ? ? len = strlen(valp); > - > - ? ? ? ? ? ? ? if (len > maxlen) > - ? ? ? ? ? ? ? ? ? ? ? return -ENOBUFS; > - > - ? ? ? ? ? ? ? memcpy(elem->val, valp, len); > - ? ? ? } else { > - ? ? ? ? ? ? ? len = 0; > - ? ? ? } > - > +done: > ? ? ? ?elem->id = htonl(id); > ? ? ? ?elem->charset = htons(0x6A); /* Always use UTF-8 */ > ? ? ? ?elem->len = htons(len); > @@ -735,57 +464,22 @@ static int mp_get_media_attribute(struct media_player *mp, > ? ? ? ?return sizeof(struct media_info_elem) + len; > ?} > > -static void mp_set_attribute(struct media_player *mp, > +static int player_set_attribute(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t attr, uint8_t val) > ?{ > ? ? ? ?DBG("Change attribute: %u %u", attr, val); > > - ? ? ? mp->settings[attr] = val; > + ? ? ? return player->cb->set_setting(attr, val, player->user_data); > ?} > > -static int mp_get_attribute(struct media_player *mp, uint8_t attr) > +static int player_get_attribute(struct avrcp_player *player, uint8_t attr) > ?{ > ? ? ? ?DBG("Get attribute: %u", attr); > > - ? ? ? return mp->settings[attr]; > -} > - > -static void mp_set_media_attributes(struct media_player *mp, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct media_info *mi) > -{ > - ? ? ? g_free(mp->mi.title); > - ? ? ? mp->mi.title = g_strdup(mi->title); > - > - ? ? ? g_free(mp->mi.artist); > - ? ? ? mp->mi.artist = g_strdup(mi->artist); > - > - ? ? ? g_free(mp->mi.album); > - ? ? ? mp->mi.album = g_strdup(mi->album); > - > - ? ? ? g_free(mp->mi.genre); > - ? ? ? mp->mi.genre = g_strdup(mi->genre); > - > - ? ? ? mp->mi.ntracks = mi->ntracks; > - ? ? ? mp->mi.track = mi->track; > - ? ? ? mp->mi.track_len = mi->track_len; > - > - ? ? ? /* > - ? ? ? ?* elapsed is special. Whenever the track changes, we reset it to 0, > - ? ? ? ?* so client doesn't have to make another call to change_playback > - ? ? ? ?*/ > - ? ? ? mp->mi.elapsed = 0; > - ? ? ? g_timer_start(mp->timer); > - > - ? ? ? DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n" > - ? ? ? ? ? ? ? ? ? ? ? "\tgenre: %s\n\tNumber of tracks: %u\n" > - ? ? ? ? ? ? ? ? ? ? ? "\tTrack number: %u\n\tTrack duration: %u", > - ? ? ? ? ? ? ? ? ? ? ? mi->title, mi->artist, mi->album, mi->genre, > - ? ? ? ? ? ? ? ? ? ? ? mi->ntracks, mi->track, mi->track_len); > - > - ? ? ? avrcp_send_event(mp, AVRCP_EVENT_TRACK_CHANGED, NULL); > + ? ? ? return player->cb->get_setting(attr, player->user_data); > ?} > > -static uint8_t avrcp_handle_get_capabilities(struct media_player *mp, > +static uint8_t avrcp_handle_get_capabilities(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -811,7 +505,7 @@ static uint8_t avrcp_handle_get_capabilities(struct media_player *mp, > ? ? ? ?case CAP_EVENTS_SUPPORTED: > ? ? ? ? ? ? ? ?pdu->params_len = htons(4); > ? ? ? ? ? ? ? ?pdu->params[1] = 2; > - ? ? ? ? ? ? ? pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED; > + ? ? ? ? ? ? ? pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED; > ? ? ? ? ? ? ? ?pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED; > > ? ? ? ? ? ? ? ?return AVC_CTYPE_STABLE; > @@ -824,7 +518,7 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp, > +static uint8_t avrcp_handle_list_player_attributes(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -837,11 +531,11 @@ static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp, > ? ? ? ? ? ? ? ?return AVC_CTYPE_REJECTED; > ? ? ? ?} > > - ? ? ? if (!mp) > + ? ? ? if (!player) > ? ? ? ? ? ? ? ?goto done; > > - ? ? ? for (i = 1; i <= PLAYER_SETTING_SCAN; i++) { > - ? ? ? ? ? ? ? if (!mp_get_attribute(mp, i)) { > + ? ? ? for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) { > + ? ? ? ? ? ? ? if (player_get_attribute(player, i) < 0) { good catch. > ? ? ? ? ? ? ? ? ? ? ? ?DBG("Ignoring setting %u: not supported by player", i); > ? ? ? ? ? ? ? ? ? ? ? ?continue; > ? ? ? ? ? ? ? ?} > @@ -857,14 +551,14 @@ done: > ? ? ? ?return AVC_CTYPE_STABLE; > ?} > > -static uint8_t avrcp_handle_list_player_values(struct media_player *mp, > +static uint8_t avrcp_handle_list_player_values(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > ? ? ? ?uint16_t len = ntohs(pdu->params_len); > ? ? ? ?unsigned int i; > > - ? ? ? if (len != 1 || !mp) > + ? ? ? if (len != 1 || !player) > ? ? ? ? ? ? ? ?goto err; > > ? ? ? ?len = attr_get_max_val(pdu->params[0]); > @@ -887,7 +581,7 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp, > +static uint8_t avrcp_handle_get_element_attributes(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -910,8 +604,9 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp, > ? ? ? ? ? ? ? ? * Return all available information, at least > ? ? ? ? ? ? ? ? * title must be returned. > ? ? ? ? ? ? ? ? */ > - ? ? ? ? ? ? ? for (i = 1; i < MEDIA_INFO_LAST; i++) { > - ? ? ? ? ? ? ? ? ? ? ? size = mp_get_media_attribute(mp, i, &pdu->params[pos], > + ? ? ? ? ? ? ? for (i = 1; i < AVRCP_MEDIA_ATTRIBUTE_LAST; i++) { > + ? ? ? ? ? ? ? ? ? ? ? size = player_get_media_attribute(player, i, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &pdu->params[pos], > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AVRCP_PDU_MTU - pos); > > ? ? ? ? ? ? ? ? ? ? ? ?if (size > 0) { > @@ -927,7 +622,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp, > ? ? ? ? ? ? ? ?for (i = 0; i < nattr; i++) { > ? ? ? ? ? ? ? ? ? ? ? ?uint32_t attr = ntohl(attr_ids[i]); > > - ? ? ? ? ? ? ? ? ? ? ? size = mp_get_media_attribute(mp, attr, > + ? ? ? ? ? ? ? ? ? ? ? size = player_get_media_attribute(player, attr, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&pdu->params[pos], > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AVRCP_PDU_MTU - pos); > > @@ -953,7 +648,7 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp, > +static uint8_t avrcp_handle_get_current_player_value(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -961,7 +656,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp, > ? ? ? ?uint8_t *settings; > ? ? ? ?unsigned int i; > > - ? ? ? if (mp == NULL || len <= 1 || pdu->params[0] != len - 1) > + ? ? ? if (player == NULL || len <= 1 || pdu->params[0] != len - 1) > ? ? ? ? ? ? ? ?goto err; > > ? ? ? ?/* > @@ -977,16 +672,16 @@ static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp, > ? ? ? ? * non-existent we should send an error. > ? ? ? ? */ > ? ? ? ?for (i = 0; i < pdu->params[0]; i++) { > - ? ? ? ? ? ? ? uint8_t val; > + ? ? ? ? ? ? ? int val; > > - ? ? ? ? ? ? ? if (settings[i] < PLAYER_SETTING_EQUALIZER || > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? settings[i] > PLAYER_SETTING_SCAN) { > + ? ? ? ? ? ? ? if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER || > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? settings[i] > AVRCP_ATTRIBUTE_SCAN) { > ? ? ? ? ? ? ? ? ? ? ? ?DBG("Ignoring %u", settings[i]); > ? ? ? ? ? ? ? ? ? ? ? ?continue; > ? ? ? ? ? ? ? ?} > > - ? ? ? ? ? ? ? val = mp_get_attribute(mp, settings[i]); > - ? ? ? ? ? ? ? if (!val) { > + ? ? ? ? ? ? ? val = player_get_attribute(player, settings[i]); > + ? ? ? ? ? ? ? if (val < 0) { > ? ? ? ? ? ? ? ? ? ? ? ?DBG("Ignoring %u: not supported by player", > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?settings[i]); > ? ? ? ? ? ? ? ? ? ? ? ?continue; > @@ -1014,14 +709,14 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_set_player_value(struct media_player *mp, > +static uint8_t avrcp_handle_set_player_value(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > ? ? ? ?uint16_t len = ntohs(pdu->params_len); > ? ? ? ?unsigned int i; > > - ? ? ? if (len <= 3) > + ? ? ? if (len < 3) > ? ? ? ? ? ? ? ?goto err; > > ? ? ? ?len = 0; > @@ -1033,26 +728,14 @@ static uint8_t avrcp_handle_set_player_value(struct media_player *mp, > ? ? ? ? * attribute is valid, we respond with no parameters. Otherwise an > ? ? ? ? * E_INVALID_PARAM is sent. > ? ? ? ? */ > - ? ? ? for (i = 1; i < pdu->params[0]; i += 2) { > + ? ? ? for (i = 1; i <= pdu->params[0]; i += 2) { Not the right fix. But I'm sending my patch on top of this, since the bug was already there. > ? ? ? ? ? ? ? ?uint8_t attr = pdu->params[i]; > ? ? ? ? ? ? ? ?uint8_t val = pdu->params[i + 1]; > - ? ? ? ? ? ? ? const char *attrstr; > - ? ? ? ? ? ? ? const char *valstr; > - > - ? ? ? ? ? ? ? attrstr = attr_to_str(attr); > - ? ? ? ? ? ? ? if (!attrstr) > - ? ? ? ? ? ? ? ? ? ? ? continue; > > - ? ? ? ? ? ? ? valstr = attrval_to_str(attr, val); > - ? ? ? ? ? ? ? if (!valstr) > + ? ? ? ? ? ? ? if (player_set_attribute(player, attr, val) < 0) > ? ? ? ? ? ? ? ? ? ? ? ?continue; > > ? ? ? ? ? ? ? ?len++; > - > - ? ? ? ? ? ? ? mp_set_attribute(mp, attr, val); > - ? ? ? ? ? ? ? emit_property_changed(mp->dev->conn, mp->dev->path, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, attrstr, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING, &valstr); > ? ? ? ?} > > ? ? ? ?if (len) { > @@ -1067,7 +750,7 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_displayable_charset(struct media_player *mp, > +static uint8_t avrcp_handle_displayable_charset(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -1087,7 +770,7 @@ static uint8_t avrcp_handle_displayable_charset(struct media_player *mp, > ? ? ? ?return AVC_CTYPE_STABLE; > ?} > > -static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp, > +static uint8_t avrcp_handle_ct_battery_status(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > @@ -1101,9 +784,6 @@ static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp, > ? ? ? ?if (valstr == NULL) > ? ? ? ? ? ? ? ?goto err; > > - ? ? ? emit_property_changed(mp->dev->conn, mp->dev->path, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, "Battery", > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING, &valstr); > ? ? ? ?pdu->params_len = 0; > > ? ? ? ?return AVC_CTYPE_STABLE; > @@ -1114,14 +794,13 @@ err: > ? ? ? ?return AVC_CTYPE_REJECTED; > ?} > > -static uint8_t avrcp_handle_get_play_status(struct media_player *mp, > +static uint8_t avrcp_handle_get_play_status(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > ? ? ? ?uint16_t len = ntohs(pdu->params_len); > - ? ? ? uint32_t elapsed; > - ? ? ? uint32_t track_len; > - ? ? ? uint8_t status; > + ? ? ? uint32_t position; > + ? ? ? uint32_t duration; > > ? ? ? ?if (len != 0) { > ? ? ? ? ? ? ? ?pdu->params_len = htons(1); > @@ -1129,25 +808,28 @@ static uint8_t avrcp_handle_get_play_status(struct media_player *mp, > ? ? ? ? ? ? ? ?return AVC_CTYPE_REJECTED; > ? ? ? ?} > > - ? ? ? mp_get_playback_status(mp, &status, &elapsed, &track_len); > - ? ? ? track_len = htonl(track_len); > - ? ? ? elapsed = htonl(elapsed); > + ? ? ? position = player->cb->get_position(player->user_data); > + ? ? ? duration = GPOINTER_TO_UINT(player->cb->get_metadata( > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AVRCP_MEDIA_ATTRIBUTE_DURATION, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? player->user_data)); > + > + ? ? ? duration = htonl(duration); > + ? ? ? position = htonl(position); > > - ? ? ? memcpy(&pdu->params[0], &track_len, 4); > - ? ? ? memcpy(&pdu->params[4], &elapsed, 4); > - ? ? ? pdu->params[8] = status; > + ? ? ? memcpy(&pdu->params[0], &duration, 4); > + ? ? ? memcpy(&pdu->params[4], &position, 4); > + ? ? ? pdu->params[8] = player->cb->get_status(player->user_data);; > > ? ? ? ?pdu->params_len = htons(9); > > ? ? ? ?return AVC_CTYPE_STABLE; > ?} > > -static uint8_t avrcp_handle_register_notification(struct media_player *mp, > +static uint8_t avrcp_handle_register_notification(struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction) > ?{ > ? ? ? ?uint16_t len = ntohs(pdu->params_len); > - ? ? ? uint8_t status; > > ? ? ? ?/* > ? ? ? ? * 1 byte for EventID, 4 bytes for Playback interval but the latest > @@ -1158,10 +840,9 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp, > ? ? ? ? ? ? ? ?goto err; > > ? ? ? ?switch (pdu->params[0]) { > - ? ? ? case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED: > + ? ? ? case AVRCP_EVENT_STATUS_CHANGED: > ? ? ? ? ? ? ? ?len = 2; > - ? ? ? ? ? ? ? mp_get_playback_status(mp, &status, NULL, NULL); > - ? ? ? ? ? ? ? pdu->params[1] = status; > + ? ? ? ? ? ? ? pdu->params[1] = player->cb->get_status(player->user_data); > > ? ? ? ? ? ? ? ?break; > ? ? ? ?case AVRCP_EVENT_TRACK_CHANGED: > @@ -1176,8 +857,8 @@ static uint8_t avrcp_handle_register_notification(struct media_player *mp, > ? ? ? ?} > > ? ? ? ?/* Register event and save the transaction used */ > - ? ? ? mp->registered_events |= (1 << pdu->params[0]); > - ? ? ? mp->transaction_events[pdu->params[0]] = transaction; > + ? ? ? player->registered_events |= (1 << pdu->params[0]); > + ? ? ? player->transaction_events[pdu->params[0]] = transaction; > > ? ? ? ?pdu->params_len = htons(len); > > @@ -1192,7 +873,7 @@ err: > ?static struct pdu_handler { > ? ? ? ?uint8_t pdu_id; > ? ? ? ?uint8_t code; > - ? ? ? uint8_t (*func) (struct media_player *mp, > + ? ? ? uint8_t (*func) (struct avrcp_player *player, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct avrcp_header *pdu, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t transaction); > ?} handlers[] = { > @@ -1229,7 +910,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint8_t *operands, size_t operand_count, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?void *user_data) > ?{ > - ? ? ? struct media_player *mp = user_data; > + ? ? ? struct avrcp_player *player = user_data; > ? ? ? ?struct pdu_handler *handler; > ? ? ? ?struct avrcp_header *pdu = (void *) operands; > ? ? ? ?uint32_t company_id = get_company_id(pdu->company_id); > @@ -1265,7 +946,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction, > ? ? ? ? ? ? ? ?goto err_metadata; > ? ? ? ?} > > - ? ? ? *code = handler->func(mp, pdu, transaction); > + ? ? ? *code = handler->func(player, pdu, transaction); > > ? ? ? ?return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len); > > @@ -1276,52 +957,56 @@ err_metadata: > ? ? ? ?return AVRCP_HEADER_LENGTH + 1; > ?} > > +static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src) > +{ > + ? ? ? for (; list; list = list->next) { > + ? ? ? ? ? ? ? struct avrcp_server *server = list->data; > + > + ? ? ? ? ? ? ? if (bacmp(&server->src, src) == 0) > + ? ? ? ? ? ? ? ? ? ? ? return server; > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > ?static void state_changed(struct audio_device *dev, avctp_state_t old_state, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?avctp_state_t new_state, void *user_data) > ?{ > - ? ? ? struct media_player *mp = dev->media_player; > + ? ? ? struct avrcp_server *server; > + ? ? ? struct avrcp_player *player; > > + ? ? ? server = find_server(servers, &dev->src); > + ? ? ? if (!server) > + ? ? ? ? ? ? ? return; > > - ? ? ? if (!mp) > + ? ? ? player = server->active_player; > + ? ? ? if (!player) > ? ? ? ? ? ? ? ?return; > > ? ? ? ?switch (new_state) { > ? ? ? ?case AVCTP_STATE_DISCONNECTED: > - ? ? ? ? ? ? ? mp->session = NULL; > + ? ? ? ? ? ? ? player->session = NULL; > > - ? ? ? ? ? ? ? if (mp->handler) { > - ? ? ? ? ? ? ? ? ? ? ? avctp_unregister_pdu_handler(mp->handler); > - ? ? ? ? ? ? ? ? ? ? ? mp->handler = 0; > + ? ? ? ? ? ? ? if (player->handler) { > + ? ? ? ? ? ? ? ? ? ? ? avctp_unregister_pdu_handler(player->handler); > + ? ? ? ? ? ? ? ? ? ? ? player->handler = 0; > ? ? ? ? ? ? ? ?} > > ? ? ? ? ? ? ? ?break; > ? ? ? ?case AVCTP_STATE_CONNECTING: > - ? ? ? ? ? ? ? mp->session = avctp_connect(&dev->src, &dev->dst); > + ? ? ? ? ? ? ? player->session = avctp_connect(&dev->src, &dev->dst); > > - ? ? ? ? ? ? ? if (!mp->handler) > - ? ? ? ? ? ? ? ? ? ? ? mp->handler = avctp_register_pdu_handler( > + ? ? ? ? ? ? ? if (!player->handler) > + ? ? ? ? ? ? ? ? ? ? ? player->handler = avctp_register_pdu_handler( > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AVC_OP_VENDORDEP, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?handle_vendordep_pdu, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mp); > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? player); > ? ? ? ? ? ? ? ?break; > ? ? ? ?default: > ? ? ? ? ? ? ? ?return; > ? ? ? ?} > ?} > > -static void media_info_init(struct media_info *mi) > -{ > - ? ? ? memset(mi, 0, sizeof(*mi)); > - > - ? ? ? /* > - ? ? ? ?* As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG > - ? ? ? ?* does not support these attributes (i.e. they were never set via > - ? ? ? ?* D-Bus) > - ? ? ? ?*/ > - ? ? ? mi->track_len = 0xFFFFFFFF; > - ? ? ? mi->elapsed = 0xFFFFFFFF; > -} > - > ?gboolean avrcp_connect(struct audio_device *dev) > ?{ > ? ? ? ?struct avctp *session; > @@ -1344,8 +1029,6 @@ void avrcp_disconnect(struct audio_device *dev) > ? ? ? ?avctp_disconnect(session); > ?} > > -static unsigned int avctp_id = 0; > - > ?int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config) > ?{ > ? ? ? ?sdp_record_t *record; > @@ -1390,7 +1073,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config) > ? ? ? ?} > > ? ? ? ?if (add_record_to_server(src, record) < 0) { > - ? ? ? ? ? ? ? error("Unable to register AVRCP mpler service record"); > + ? ? ? ? ? ? ? error("Unable to register AVRCP service record"); > ? ? ? ? ? ? ? ?sdp_record_free(record); > ? ? ? ? ? ? ? ?g_free(server); > ? ? ? ? ? ? ? ?return -1; > @@ -1411,16 +1094,17 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config) > ? ? ? ?return 0; > ?} > > -static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src) > +static void player_destroy(gpointer data) > ?{ > - ? ? ? for (; list; list = list->next) { > - ? ? ? ? ? ? ? struct avrcp_server *server = list->data; > + ? ? ? struct avrcp_player *player = data; > > - ? ? ? ? ? ? ? if (bacmp(&server->src, src) == 0) > - ? ? ? ? ? ? ? ? ? ? ? return server; > - ? ? ? } > + ? ? ? if (player->destroy) > + ? ? ? ? ? ? ? player->destroy(player->user_data); > > - ? ? ? return NULL; > + ? ? ? if (player->handler) > + ? ? ? ? ? ? ? avctp_unregister_pdu_handler(player->handler); > + > + ? ? ? g_free(player); > ?} > > ?void avrcp_unregister(const bdaddr_t *src) > @@ -1431,6 +1115,8 @@ void avrcp_unregister(const bdaddr_t *src) > ? ? ? ?if (!server) > ? ? ? ? ? ? ? ?return; > > + ? ? ? g_slist_free_full(server->players, player_destroy); > + > ? ? ? ?servers = g_slist_remove(servers, server); > > ? ? ? ?remove_record_from_server(server->ct_record_id); > @@ -1442,239 +1128,49 @@ void avrcp_unregister(const bdaddr_t *src) > ? ? ? ?if (servers) > ? ? ? ? ? ? ? ?return; > > - ? ? ? if (avctp_id) > + ? ? ? if (avctp_id) { > ? ? ? ? ? ? ? ?avctp_remove_state_cb(avctp_id); > -} > - > -static DBusMessage *mp_set_property(DBusConnection *conn, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessage *msg, void *data) > -{ > - ? ? ? struct audio_device *device = data; > - ? ? ? struct media_player *mp = device->media_player; > - ? ? ? DBusMessageIter iter; > - ? ? ? DBusMessageIter var; > - ? ? ? const char *attrstr, *valstr; > - ? ? ? int attr, val; > - > - ? ? ? if (!dbus_message_iter_init(msg, &iter)) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? dbus_message_iter_get_basic(&iter, &attrstr); > - > - ? ? ? attr = attr_to_val(attrstr); > - ? ? ? if (attr < 0) > - ? ? ? ? ? ? ? return btd_error_not_supported(msg); > - > - ? ? ? dbus_message_iter_next(&iter); > - > - ? ? ? if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? dbus_message_iter_recurse(&iter, &var); > - > - ? ? ? /* Only string arguments are supported for now */ > - ? ? ? if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? dbus_message_iter_get_basic(&var, &valstr); > - > - ? ? ? val = attrval_to_val(attr, valstr); > - ? ? ? if (val < 0) > - ? ? ? ? ? ? ? return btd_error_not_supported(msg); > - > - ? ? ? mp_set_attribute(mp, attr, val); > - > - ? ? ? return dbus_message_new_method_return(msg); > -} > - > -static DBusMessage *mp_change_playback(DBusConnection *conn, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessage *msg, void *data) > -{ > - ? ? ? struct audio_device *device = data; > - ? ? ? struct media_player *mp = device->media_player; > - ? ? ? const char *statusstr; > - ? ? ? int status; > - ? ? ? uint32_t elapsed; > - > - ? ? ? if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_UINT32, &elapsed, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID)) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? status = play_status_to_val(statusstr); > - ? ? ? if (status < 0) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? mp_set_playback_status(mp, status, elapsed); > - > - ? ? ? return dbus_message_new_method_return(msg); > -} > - > -static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi) > -{ > - ? ? ? DBusMessageIter dict; > - ? ? ? DBusMessageIter var; > - ? ? ? int ctype; > - > - ? ? ? ctype = dbus_message_iter_get_arg_type(iter); > - ? ? ? if (ctype != DBUS_TYPE_ARRAY) > - ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? media_info_init(mi); > - ? ? ? dbus_message_iter_recurse(iter, &dict); > - > - ? ? ? while ((ctype = dbus_message_iter_get_arg_type(&dict)) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID) { > - ? ? ? ? ? ? ? DBusMessageIter entry; > - ? ? ? ? ? ? ? const char *key; > - > - ? ? ? ? ? ? ? if (ctype != DBUS_TYPE_DICT_ENTRY) > - ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? dbus_message_iter_recurse(&dict, &entry); > - ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? dbus_message_iter_get_basic(&entry, &key); > - ? ? ? ? ? ? ? dbus_message_iter_next(&entry); > - > - ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) > - ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? dbus_message_iter_recurse(&entry, &var); > - > - ? ? ? ? ? ? ? if (!strcmp(key, "Title")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->title); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "Artist")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->artist); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "Album")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->album); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "Genre")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->genre); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "NumberOfTracks")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_UINT32) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->ntracks); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "TrackNumber")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_UINT32) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->track); > - ? ? ? ? ? ? ? } else if (!strcmp(key, "TrackDuration")) { > - ? ? ? ? ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&var) != > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_UINT32) > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &mi->track_len); > - ? ? ? ? ? ? ? } else { > - ? ? ? ? ? ? ? ? ? ? ? return FALSE; > - ? ? ? ? ? ? ? } > - > - ? ? ? ? ? ? ? dbus_message_iter_next(&dict); > + ? ? ? ? ? ? ? avctp_id = 0; > ? ? ? ?} > - > - ? ? ? if (mi->title == NULL) > - ? ? ? ? ? ? ? return FALSE; > - > - ? ? ? return TRUE; > ?} > > -static DBusMessage *mp_change_track(DBusConnection *conn, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessage *msg, void *data) > +struct avrcp_player *avrcp_register_player(const bdaddr_t *src, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct avrcp_player_cb *cb, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *user_data, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GDestroyNotify destroy) > ?{ > - ? ? ? struct audio_device *device = data; > - ? ? ? struct media_player *mp = device->media_player; > - ? ? ? DBusMessageIter iter; > - ? ? ? struct media_info mi; > - > - > - ? ? ? dbus_message_iter_init(msg, &iter); > - ? ? ? if (!media_info_parse(&iter, &mi)) > - ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > - > - ? ? ? mp_set_media_attributes(mp, &mi); > - > - ? ? ? return dbus_message_new_method_return(msg); > -} > - > -static GDBusMethodTable mp_methods[] = { > - ? ? ? { "SetProperty", ? ? ? ?"sv", ? ? ? ? ? "", ? ? mp_set_property }, > - ? ? ? { "ChangePlayback", ? ? "su", ? ? ? ? ? "", ? ? mp_change_playback }, > - ? ? ? { "ChangeTrack", ? ? ? ?"a{sv}", ? ? ? ?"", ? ? mp_change_track }, > - ? ? ? { } > -}; > + ? ? ? struct avrcp_server *server; > + ? ? ? struct avrcp_player *player; > > -static GDBusSignalTable mp_signals[] = { > - ? ? ? { "PropertyChanged", ? ? ? ? ? ?"sv" ? ?}, > - ? ? ? { } > -}; > + ? ? ? server = find_server(servers, src); > + ? ? ? if (!server) > + ? ? ? ? ? ? ? return NULL; > > -static void mp_path_unregister(void *data) > -{ > - ? ? ? struct audio_device *dev = data; > - ? ? ? struct media_player *mp = dev->media_player; > + ? ? ? player = g_new0(struct avrcp_player, 1); > + ? ? ? player->server = server; > + ? ? ? player->cb = cb; > + ? ? ? player->user_data = user_data; > + ? ? ? player->destroy = destroy; > > - ? ? ? DBG("Unregistered interface %s on path %s", > - ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, dev->path); > + ? ? ? if (!server->players) > + ? ? ? ? ? ? ? server->active_player = player; > > - ? ? ? if (mp->handler) > - ? ? ? ? ? ? ? avctp_unregister_pdu_handler(mp->handler); > + ? ? ? if (!avctp_id) > + ? ? ? ? ? ? ? avctp_id = avctp_add_state_cb(state_changed, NULL); > > - ? ? ? g_timer_destroy(mp->timer); > - ? ? ? g_free(mp); > -} > + ? ? ? server->players = g_slist_append(server->players, player); > > -void media_player_unregister(struct audio_device *dev) > -{ > - ? ? ? g_dbus_unregister_interface(dev->conn, dev->path, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE); > + ? ? ? return player; > ?} > > -struct media_player *media_player_init(struct audio_device *dev) > +void avrcp_unregister_player(struct avrcp_player *player) > ?{ > - ? ? ? struct media_player *mp; > - > - ? ? ? if (!g_dbus_register_interface(dev->conn, dev->path, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mp_methods, mp_signals, NULL, > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dev, mp_path_unregister)) { > - ? ? ? ? ? ? ? error("D-Bus failed do register %s on path %s", > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, dev->path); > - ? ? ? ? ? ? ? return NULL; > - ? ? ? } > + ? ? ? struct avrcp_server *server = player->server; > > - ? ? ? DBG("Registered interface %s on path %s", > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, dev->path); > + ? ? ? server->players = g_slist_remove(server->players, player); > > - ? ? ? mp = g_new0(struct media_player, 1); > - ? ? ? mp->timer = g_timer_new(); > - ? ? ? mp->dev = dev; > - ? ? ? media_info_init(&mp->mi); > - > - ? ? ? if (!avctp_id) > - ? ? ? ? ? ? ? avctp_id = avctp_add_state_cb(state_changed, NULL); > + ? ? ? if (server->active_player == player) > + ? ? ? ? ? ? ? server->active_player = g_slist_nth_data(server->players, 0); > > - ? ? ? return mp; > + ? ? ? player_destroy(player); > ?} > diff --git a/audio/avrcp.h b/audio/avrcp.h > index 1fd912d..66d09bc 100644 > --- a/audio/avrcp.h > +++ b/audio/avrcp.h > @@ -22,7 +22,63 @@ > ?* > ?*/ > > -#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer" > +/* player attributes */ > +#define AVRCP_ATTRIBUTE_ILEGAL ? ? ? ? 0x00 > +#define AVRCP_ATTRIBUTE_EQUALIZER ? ? ?0x01 > +#define AVRCP_ATTRIBUTE_REPEAT_MODE ? ?0x02 > +#define AVRCP_ATTRIBUTE_SHUFFLE ? ? ? ? ? ? ? ?0x03 > +#define AVRCP_ATTRIBUTE_SCAN ? ? ? ? ? 0x04 > + > +/* equalizer values */ > +#define AVRCP_EQUALIZER_OFF ? ? ? ? ? ?0x01 > +#define AVRCP_EQUALIZER_ON ? ? ? ? ? ? 0x02 > + > +/* repeat mode values */ > +#define AVRCP_REPEAT_MODE_OFF ? ? ? ? ?0x01 > +#define AVRCP_REPEAT_MODE_SINGLE ? ? ? 0x02 > +#define AVRCP_REPEAT_MODE_ALL ? ? ? ? ?0x03 > +#define AVRCP_REPEAT_MODE_GROUP ? ? ? ? ? ? ? ?0x04 > + > +/* shuffle values */ > +#define AVRCP_SHUFFLE_OFF ? ? ? ? ? ? ?0x01 > +#define AVRCP_SHUFFLE_ALL ? ? ? ? ? ? ?0x02 > +#define AVRCP_SHUFFLE_GROUP ? ? ? ? ? ?0x03 > + > +/* scan values */ > +#define AVRCP_SCAN_OFF ? ? ? ? ? ? ? ? 0x01 > +#define AVRCP_SCAN_ALL ? ? ? ? ? ? ? ? 0x02 > +#define AVRCP_SCAN_GROUP ? ? ? ? ? ? ? 0x03 > + > +/* media attributes */ > +#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ?0x00 > +#define AVRCP_MEDIA_ATTRIBUTE_TITLE ? ?0x01 > +#define AVRCP_MEDIA_ATTRIBUTE_ARTIST ? 0x02 > +#define AVRCP_MEDIA_ATTRIBUTE_ALBUM ? ?0x03 > +#define AVRCP_MEDIA_ATTRIBUTE_TRACK ? ?0x04 > +#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 > +#define AVRCP_MEDIA_ATTRIBUTE_GENRE ? ?0x06 > +#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 > +#define AVRCP_MEDIA_ATTRIBUTE_LAST ? ? AVRCP_MEDIA_ATTRIBUTE_DURATION > + > +/* play status */ > +#define AVRCP_PLAY_STATUS_STOPPED ? ? ?0x00 > +#define AVRCP_PLAY_STATUS_PLAYING ? ? ?0x01 > +#define AVRCP_PLAY_STATUS_PAUSED ? ? ? 0x02 > +#define AVRCP_PLAY_STATUS_FWD_SEEK ? ? 0x03 > +#define AVRCP_PLAY_STATUS_REV_SEEK ? ? 0x04 > +#define AVRCP_PLAY_STATUS_ERROR ? ? ? ? ? ? ? ?0xFF > + > +/* Notification events */ > +#define AVRCP_EVENT_STATUS_CHANGED ? ? 0x01 > +#define AVRCP_EVENT_TRACK_CHANGED ? ? ?0x02 > + > +struct avrcp_player_cb { > + ? ? ? int (*get_setting) (uint8_t attr, void *user_data); > + ? ? ? int (*set_setting) (uint8_t attr, uint8_t value, void *user_data); > + ? ? ? void *(*get_metadata) (uint32_t id, void *user_data); > + ? ? ? uint8_t (*get_status) (void *user_data); > + ? ? ? uint32_t (*get_position) (void *user_data); > +}; > > ?int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config); > ?void avrcp_unregister(const bdaddr_t *src); > @@ -30,5 +86,10 @@ void avrcp_unregister(const bdaddr_t *src); > ?gboolean avrcp_connect(struct audio_device *dev); > ?void avrcp_disconnect(struct audio_device *dev); > > -struct media_player *media_player_init(struct audio_device *dev); > -void media_player_unregister(struct audio_device *dev); > +struct avrcp_player *avrcp_register_player(const bdaddr_t *src, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct avrcp_player_cb *cb, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *user_data, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GDestroyNotify destroy); > +void avrcp_unregister_player(struct avrcp_player *player); > + > +int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data); > diff --git a/audio/device.c b/audio/device.c > index f455bcf..a9d35f9 100644 > --- a/audio/device.c > +++ b/audio/device.c > @@ -731,9 +731,6 @@ void audio_device_unregister(struct audio_device *device) > ? ? ? ?if (device->control) > ? ? ? ? ? ? ? ?control_unregister(device); > > - ? ? ? if (device->media_player) > - ? ? ? ? ? ? ? media_player_unregister(device); > - > ? ? ? ?g_dbus_unregister_interface(device->conn, device->path, > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AUDIO_INTERFACE); > > diff --git a/audio/device.h b/audio/device.h > index 9645d93..5117fca 100644 > --- a/audio/device.h > +++ b/audio/device.h > @@ -44,7 +44,6 @@ struct target; > ?struct sink; > ?struct headset; > ?struct gateway; > -struct media_player; > ?struct dev_priv; > > ?struct audio_device { > @@ -63,7 +62,6 @@ struct audio_device { > ? ? ? ?struct source *source; > ? ? ? ?struct control *control; > ? ? ? ?struct target *target; > - ? ? ? struct media_player *media_player; > > ? ? ? ?guint hs_preauth_id; > > diff --git a/audio/manager.c b/audio/manager.c > index 06d3f0e..7ec0311 100644 > --- a/audio/manager.c > +++ b/audio/manager.c > @@ -119,7 +119,6 @@ static struct enabled_interfaces enabled = { > ? ? ? ?.control ? ? ? ?= TRUE, > ? ? ? ?.socket ? ? ? ? = TRUE, > ? ? ? ?.media ? ? ? ? ?= FALSE, > - ? ? ? .media_player ? = FALSE, > ?}; > > ?static struct audio_adapter *find_adapter(GSList *list, > @@ -224,8 +223,6 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device) > ? ? ? ? ? ? ? ?else > ? ? ? ? ? ? ? ? ? ? ? ?device->control = control_init(device, uuid16); > > - ? ? ? ? ? ? ? if (enabled.media_player && !device->media_player) > - ? ? ? ? ? ? ? ? ? ? ? device->media_player = media_player_init(device); > ? ? ? ? ? ? ? ?if (device->sink && sink_is_active(device)) > ? ? ? ? ? ? ? ? ? ? ? ?avrcp_connect(device); > ? ? ? ? ? ? ? ?break; > @@ -1177,8 +1174,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf, > ? ? ? ? ? ? ? ? ? ? ? ?enabled.socket = TRUE; > ? ? ? ? ? ? ? ?else if (g_str_equal(list[i], "Media")) > ? ? ? ? ? ? ? ? ? ? ? ?enabled.media = TRUE; > - ? ? ? ? ? ? ? else if (g_str_equal(list[i], "MediaPlayer")) > - ? ? ? ? ? ? ? ? ? ? ? enabled.media_player = TRUE; > > ? ? ? ?} > ? ? ? ?g_strfreev(list); > @@ -1200,8 +1195,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf, > ? ? ? ? ? ? ? ? ? ? ? ?enabled.socket = FALSE; > ? ? ? ? ? ? ? ?else if (g_str_equal(list[i], "Media")) > ? ? ? ? ? ? ? ? ? ? ? ?enabled.media = FALSE; > - ? ? ? ? ? ? ? else if (g_str_equal(list[i], "MediaPlayer")) > - ? ? ? ? ? ? ? ? ? ? ? enabled.media_player = FALSE; > ? ? ? ?} > ? ? ? ?g_strfreev(list); > > diff --git a/audio/media.c b/audio/media.c > index 5ab3eab..9057d70 100644 > --- a/audio/media.c > +++ b/audio/media.c > @@ -42,6 +42,7 @@ > ?#include "media.h" > ?#include "transport.h" > ?#include "a2dp.h" > +#include "avrcp.h" > ?#include "headset.h" > ?#include "gateway.h" > ?#include "manager.h" > @@ -52,6 +53,7 @@ > > ?#define MEDIA_INTERFACE "org.bluez.Media" > ?#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint" > +#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer" > > ?#define REQUEST_TIMEOUT (3 * 1000) ? ? ? ? ? ? /* 3 seconds */ > > @@ -60,6 +62,7 @@ struct media_adapter { > ? ? ? ?char ? ? ? ? ? ? ? ? ? ?*path; ? ? ? ? ?/* Adapter path */ > ? ? ? ?DBusConnection ? ? ? ? ?*conn; ? ? ? ? ?/* Adapter connection */ > ? ? ? ?GSList ? ? ? ? ? ? ? ? ?*endpoints; ? ? /* Endpoints list */ > + ? ? ? GSList ? ? ? ? ? ? ? ? ?*players; ? ? ? /* Players list */ > ?}; > > ?struct endpoint_request { > @@ -86,6 +89,29 @@ struct media_endpoint { > ? ? ? ?struct media_adapter ? ?*adapter; > ?}; > > +struct media_player { > + ? ? ? struct media_adapter ? ?*adapter; > + ? ? ? struct avrcp_player ? ? *player; > + ? ? ? char ? ? ? ? ? ? ? ? ? ?*sender; ? ? ? ?/* Player DBus bus id */ > + ? ? ? char ? ? ? ? ? ? ? ? ? ?*path; ? ? ? ? ?/* Player object path */ > + ? ? ? GHashTable ? ? ? ? ? ? ?*settings; ? ? ?/* Player settings */ > + ? ? ? GHashTable ? ? ? ? ? ? ?*track; ? ? ? ? /* Player current track */ > + ? ? ? guint ? ? ? ? ? ? ? ? ? watch; > + ? ? ? guint ? ? ? ? ? ? ? ? ? property_watch; > + ? ? ? guint ? ? ? ? ? ? ? ? ? track_watch; > + ? ? ? uint8_t ? ? ? ? ? ? ? ? status; > + ? ? ? uint32_t ? ? ? ? ? ? ? ?position; > + ? ? ? GTimer ? ? ? ? ? ? ? ? ?*timer; > +}; > + > +struct metadata_value { > + ? ? ? int ? ? ? ? ? ? ? ? ? ? type; > + ? ? ? union { > + ? ? ? ? ? ? ? char ? ? ? ? ? ?*str; > + ? ? ? ? ? ? ? uint32_t ? ? ? ?num; > + ? ? ? } value; > +}; > + > ?static GSList *adapters = NULL; > > ?static void endpoint_request_free(struct endpoint_request *request) > @@ -822,9 +848,767 @@ static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg, > ? ? ? ?return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); > ?} > > +static struct media_player *media_adapter_find_player( > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct media_adapter *adapter, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *sender, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *path) > +{ > + ? ? ? GSList *l; > + > + ? ? ? for (l = adapter->endpoints; l; l = l->next) { > + ? ? ? ? ? ? ? struct media_player *mp = l->data; > + > + ? ? ? ? ? ? ? if (sender && g_strcmp0(mp->sender, sender) != 0) > + ? ? ? ? ? ? ? ? ? ? ? continue; > + > + ? ? ? ? ? ? ? if (path && g_strcmp0(mp->path, path) != 0) > + ? ? ? ? ? ? ? ? ? ? ? continue; > + > + ? ? ? ? ? ? ? return mp; > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > +static void media_player_free(gpointer data) > +{ > + ? ? ? struct media_player *mp = data; > + ? ? ? struct media_adapter *adapter = mp->adapter; > + > + ? ? ? g_dbus_remove_watch(adapter->conn, mp->watch); > + ? ? ? g_dbus_remove_watch(adapter->conn, mp->property_watch); > + ? ? ? g_dbus_remove_watch(adapter->conn, mp->track_watch); > + > + ? ? ? if (mp->track) > + ? ? ? ? ? ? ? g_hash_table_unref(mp->track); > + > + ? ? ? if (mp->settings) > + ? ? ? ? ? ? ? g_hash_table_unref(mp->settings); > + > + ? ? ? g_free(mp->sender); > + ? ? ? g_free(mp->path); > + ? ? ? g_free(mp); > +} > + > +static void media_player_destroy(struct media_player *mp) > +{ > + ? ? ? DBG("sender=%s path=%s", mp->sender, mp->path); > + > + ? ? ? if (mp->player) { > + ? ? ? ? ? ? ? avrcp_unregister_player(mp->player); > + ? ? ? ? ? ? ? return; > + ? ? ? } > + > + ? ? ? media_player_free(mp); > +} > + > +static void media_player_remove(struct media_player *mp) > +{ > + ? ? ? info("Player unregistered: sender=%s path=%s", mp->sender, mp->path); > + > + ? ? ? media_player_destroy(mp); > +} > + > +static const char *attrval_to_str(uint8_t attr, uint8_t value) > +{ > + ? ? ? switch (attr) { > + ? ? ? case AVRCP_ATTRIBUTE_EQUALIZER: > + ? ? ? ? ? ? ? switch (value) { > + ? ? ? ? ? ? ? case AVRCP_EQUALIZER_ON: > + ? ? ? ? ? ? ? ? ? ? ? return "on"; > + ? ? ? ? ? ? ? case AVRCP_EQUALIZER_OFF: > + ? ? ? ? ? ? ? ? ? ? ? return "off"; > + ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? break; > + ? ? ? case AVRCP_ATTRIBUTE_REPEAT_MODE: > + ? ? ? ? ? ? ? switch (value) { > + ? ? ? ? ? ? ? case AVRCP_REPEAT_MODE_OFF: > + ? ? ? ? ? ? ? ? ? ? ? return "off"; > + ? ? ? ? ? ? ? case AVRCP_REPEAT_MODE_SINGLE: > + ? ? ? ? ? ? ? ? ? ? ? return "singletrack"; > + ? ? ? ? ? ? ? case AVRCP_REPEAT_MODE_ALL: > + ? ? ? ? ? ? ? ? ? ? ? return "alltracks"; > + ? ? ? ? ? ? ? case AVRCP_REPEAT_MODE_GROUP: > + ? ? ? ? ? ? ? ? ? ? ? return "group"; > + ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? break; > + ? ? ? /* Shuffle and scan have the same values */ > + ? ? ? case AVRCP_ATTRIBUTE_SHUFFLE: > + ? ? ? case AVRCP_ATTRIBUTE_SCAN: > + ? ? ? ? ? ? ? switch (value) { > + ? ? ? ? ? ? ? case AVRCP_SCAN_OFF: > + ? ? ? ? ? ? ? ? ? ? ? return "off"; > + ? ? ? ? ? ? ? case AVRCP_SCAN_ALL: > + ? ? ? ? ? ? ? ? ? ? ? return "alltracks"; > + ? ? ? ? ? ? ? case AVRCP_SCAN_GROUP: > + ? ? ? ? ? ? ? ? ? ? ? return "group"; > + ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? break; > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > +static int attrval_to_val(uint8_t attr, const char *value) > +{ > + ? ? ? int ret; > + > + ? ? ? switch (attr) { > + ? ? ? case AVRCP_ATTRIBUTE_EQUALIZER: > + ? ? ? ? ? ? ? if (!strcmp(value, "off")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_EQUALIZER_OFF; > + ? ? ? ? ? ? ? else if (!strcmp(value, "on")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_EQUALIZER_ON; > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > + > + ? ? ? ? ? ? ? return ret; > + ? ? ? case AVRCP_ATTRIBUTE_REPEAT_MODE: > + ? ? ? ? ? ? ? if (!strcmp(value, "off")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_REPEAT_MODE_OFF; > + ? ? ? ? ? ? ? else if (!strcmp(value, "singletrack")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_REPEAT_MODE_SINGLE; > + ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_REPEAT_MODE_ALL; > + ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_REPEAT_MODE_GROUP; > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > + > + ? ? ? ? ? ? ? return ret; > + ? ? ? case AVRCP_ATTRIBUTE_SHUFFLE: > + ? ? ? ? ? ? ? if (!strcmp(value, "off")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SHUFFLE_OFF; > + ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SHUFFLE_ALL; > + ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SHUFFLE_GROUP; > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > + > + ? ? ? ? ? ? ? return ret; > + ? ? ? case AVRCP_ATTRIBUTE_SCAN: > + ? ? ? ? ? ? ? if (!strcmp(value, "off")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SCAN_OFF; > + ? ? ? ? ? ? ? else if (!strcmp(value, "alltracks")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SCAN_ALL; > + ? ? ? ? ? ? ? else if (!strcmp(value, "group")) > + ? ? ? ? ? ? ? ? ? ? ? ret = AVRCP_SCAN_GROUP; > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL; > + > + ? ? ? ? ? ? ? return ret; > + ? ? ? } > + > + ? ? ? return -EINVAL; > +} > + > +static const char *attr_to_str(uint8_t attr) > +{ > + ? ? ? switch (attr) { > + ? ? ? case AVRCP_ATTRIBUTE_EQUALIZER: > + ? ? ? ? ? ? ? return "Equalizer"; > + ? ? ? case AVRCP_ATTRIBUTE_REPEAT_MODE: > + ? ? ? ? ? ? ? return "Repeat"; > + ? ? ? case AVRCP_ATTRIBUTE_SHUFFLE: > + ? ? ? ? ? ? ? return "Shuffle"; > + ? ? ? case AVRCP_ATTRIBUTE_SCAN: > + ? ? ? ? ? ? ? return "Scan"; > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > +static int attr_to_val(const char *str) > +{ > + ? ? ? if (!strcasecmp(str, "Equalizer")) > + ? ? ? ? ? ? ? return AVRCP_ATTRIBUTE_EQUALIZER; > + ? ? ? else if (!strcasecmp(str, "Repeat")) > + ? ? ? ? ? ? ? return AVRCP_ATTRIBUTE_REPEAT_MODE; > + ? ? ? else if (!strcasecmp(str, "Shuffle")) > + ? ? ? ? ? ? ? return AVRCP_ATTRIBUTE_SHUFFLE; > + ? ? ? else if (!strcasecmp(str, "Scan")) > + ? ? ? ? ? ? ? return AVRCP_ATTRIBUTE_SCAN; > + > + ? ? ? return -EINVAL; > +} > + > +static int play_status_to_val(const char *status) > +{ > + ? ? ? if (!strcasecmp(status, "stopped")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_STOPPED; > + ? ? ? else if (!strcasecmp(status, "playing")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_PLAYING; > + ? ? ? else if (!strcasecmp(status, "paused")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_PAUSED; > + ? ? ? else if (!strcasecmp(status, "forward-seek")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_FWD_SEEK; > + ? ? ? else if (!strcasecmp(status, "reverse-seek")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_REV_SEEK; > + ? ? ? else if (!strcasecmp(status, "error")) > + ? ? ? ? ? ? ? return AVRCP_PLAY_STATUS_ERROR; > + > + ? ? ? return -EINVAL; > +} > + > +static int metadata_to_val(const char *str) > +{ > + ? ? ? if (!strcasecmp(str, "Title")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_TITLE; > + ? ? ? else if (!strcasecmp(str, "Artist")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_ARTIST; > + ? ? ? else if (!strcasecmp(str, "Album")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_ALBUM; > + ? ? ? else if (!strcasecmp(str, "Genre")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_GENRE; > + ? ? ? else if (!strcasecmp(str, "NumberOfTracks")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS; > + ? ? ? else if (!strcasecmp(str, "Number")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_TRACK; > + ? ? ? else if (!strcasecmp(str, "Duration")) > + ? ? ? ? ? ? ? return AVRCP_MEDIA_ATTRIBUTE_DURATION; > + > + ? ? ? return -EINVAL; > +} > + > +static const char *metadata_to_str(uint32_t id) > +{ > + ? ? ? switch (id) { > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TITLE: > + ? ? ? ? ? ? ? return "Title"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ARTIST: > + ? ? ? ? ? ? ? return "Artist"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ALBUM: > + ? ? ? ? ? ? ? return "Album"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_GENRE: > + ? ? ? ? ? ? ? return "Genre"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TRACK: > + ? ? ? ? ? ? ? return "Track"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: > + ? ? ? ? ? ? ? return "NumberOfTracks"; > + ? ? ? case AVRCP_MEDIA_ATTRIBUTE_DURATION: > + ? ? ? ? ? ? ? return "Duration"; > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > +static int get_setting(uint8_t attr, void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? void *value; > + > + ? ? ? DBG("%s", attr_to_str(attr)); > + > + ? ? ? value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr)); > + ? ? ? if (!value) > + ? ? ? ? ? ? ? return -EINVAL; > + > + ? ? ? return GPOINTER_TO_UINT(value); > +} > + > +static int set_setting(uint8_t attr, uint8_t val, void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? struct media_adapter *adapter = mp->adapter; > + ? ? ? const char *property, *value; > + ? ? ? DBusMessage *msg; > + ? ? ? DBusMessageIter iter, var; > + > + ? ? ? property = attr_to_str(attr); > + ? ? ? value = attrval_to_str(attr, val); > + > + ? ? ? DBG("%s = %s", property, value); > + > + ? ? ? if (property == NULL || value == NULL) > + ? ? ? ? ? ? ? return -EINVAL; > + > + ? ? ? msg = dbus_message_new_method_call(mp->sender, mp->path, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEDIA_PLAYER_INTERFACE, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "SetProperty"); > + ? ? ? if (msg == NULL) { > + ? ? ? ? ? ? ? error("Couldn't allocate D-Bus message"); > + ? ? ? ? ? ? ? return -ENOMEM; > + ? ? ? } > + > + ? ? ? dbus_message_iter_init_append(msg, &iter); > + ? ? ? dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property); > + > + ? ? ? dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING_AS_STRING, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &var); > + ? ? ? dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &value); > + ? ? ? dbus_message_iter_close_container(&iter, &var); > + > + ? ? ? g_dbus_send_message(adapter->conn, msg); > + > + ? ? ? return 0; > +} > + > +static void *get_metadata(uint32_t id, void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? struct metadata_value *value; > + > + ? ? ? DBG("%s", metadata_to_str(id)); > + > + ? ? ? if (mp->track == NULL) > + ? ? ? ? ? ? ? return NULL; Good. > + > + ? ? ? value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id)); > + ? ? ? if (!value) > + ? ? ? ? ? ? ? return NULL; > + > + ? ? ? switch (value->type) { > + ? ? ? case DBUS_TYPE_STRING: > + ? ? ? ? ? ? ? return value->value.str; > + ? ? ? case DBUS_TYPE_UINT32: > + ? ? ? ? ? ? ? return GUINT_TO_POINTER(value->value.num); > + ? ? ? } > + > + ? ? ? return NULL; > +} > + > +static uint8_t get_status(void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + > + ? ? ? return mp->status; > +} > + > +static uint32_t get_position(void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? double timedelta; > + ? ? ? uint32_t sec, msec; > + > + ? ? ? if (mp->status != AVRCP_PLAY_STATUS_PLAYING) > + ? ? ? ? ? ? ? return mp->position; > + > + ? ? ? timedelta = g_timer_elapsed(mp->timer, NULL); > + > + ? ? ? sec = (uint32_t) timedelta; > + ? ? ? msec = (uint32_t) ((timedelta - sec) * 1000); > + > + ? ? ? return mp->position + sec * 1000 + msec; > +} > + > +static struct avrcp_player_cb player_cb = { > + ? ? ? .get_setting = get_setting, > + ? ? ? .set_setting = set_setting, > + ? ? ? .get_metadata = get_metadata, > + ? ? ? .get_position = get_position, > + ? ? ? .get_status = get_status > +}; > + > +static void media_player_exit(DBusConnection *connection, void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + > + ? ? ? mp->watch = 0; > + ? ? ? media_player_remove(mp); > +} > + > +static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) > +{ > + ? ? ? const char *value; > + ? ? ? int val; > + > + ? ? ? if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_get_basic(iter, &value); > + ? ? ? DBG("Status=%s", value); > + > + ? ? ? val = play_status_to_val(value); > + ? ? ? if (val < 0) { > + ? ? ? ? ? ? ? error("Invalid status"); > + ? ? ? ? ? ? ? return FALSE; > + ? ? ? } > + > + ? ? ? if (mp->status == val) > + ? ? ? ? ? ? ? return TRUE; > + > + ? ? ? mp->position = get_position(mp); > + ? ? ? g_timer_start(mp->timer); > + > + ? ? ? mp->status = val; > + > + ? ? ? avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val); > + > + ? ? ? return TRUE; > +} > + > +static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) > +{ > + ? ? ? uint32_t value; > + > + ? ? ? if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_get_basic(iter, &value); > + ? ? ? DBG("Position=%u", value); > + > + ? ? ? mp->position = value; > + ? ? ? g_timer_start(mp->timer); > + > + ? ? ? return TRUE; > +} > + > +static gboolean set_property(struct media_player *mp, const char *key, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessageIter *entry) > +{ > + ? ? ? DBusMessageIter var; > + ? ? ? const char *value; > + ? ? ? int attr, val; > + > + ? ? ? if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_recurse(entry, &var); > + > + ? ? ? if (strcasecmp(key, "Status") == 0) > + ? ? ? ? ? ? ? return set_status(mp, &var); > + > + ? ? ? if (strcasecmp(key, "Position") == 0) > + ? ? ? ? ? ? ? return set_position(mp, &var); > + > + ? ? ? attr = attr_to_val(key); > + ? ? ? if (attr < 0) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_get_basic(&var, &value); > + > + ? ? ? val = attrval_to_val(attr, value); > + ? ? ? if (val < 0) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? DBG("%s=%s", key, value); > + > + ? ? ? if (!mp->settings) > + ? ? ? ? ? ? ? mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal); > + > + ? ? ? g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr), > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GUINT_TO_POINTER(val)); > + > + ? ? ? return TRUE; > +} > + > +static gboolean property_changed(DBusConnection *connection, DBusMessage *msg, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? DBusMessageIter iter; > + ? ? ? const char *property; > + > + ? ? ? DBG("sender=%s path=%s", mp->sender, mp->path); > + > + ? ? ? dbus_message_iter_init(msg, &iter); > + > + ? ? ? if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { > + ? ? ? ? ? ? ? error("Unexpected signature in %s.%s signal", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dbus_message_get_interface(msg), > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dbus_message_get_member(msg)); > + ? ? ? ? ? ? ? return TRUE; > + ? ? ? } > + > + ? ? ? dbus_message_iter_get_basic(&iter, &property); > + > + ? ? ? dbus_message_iter_next(&iter); > + > + ? ? ? set_property(mp, property, &iter); > + > + ? ? ? return TRUE; > +} > + > +static void metadata_value_free(gpointer data) > +{ > + ? ? ? struct metadata_value *value = data; > + > + ? ? ? switch (value->type) { > + ? ? ? case DBUS_TYPE_STRING: > + ? ? ? ? ? ? ? g_free(value->value.str); > + ? ? ? ? ? ? ? break; > + ? ? ? } > + > + ? ? ? g_free(value); > +} > + > +static gboolean parse_player_metadata(struct media_player *mp, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessageIter *iter) > +{ > + ? ? ? DBusMessageIter dict; > + ? ? ? DBusMessageIter var; > + ? ? ? int ctype; > + ? ? ? gboolean title = FALSE; > + > + ? ? ? ctype = dbus_message_iter_get_arg_type(iter); > + ? ? ? if (ctype != DBUS_TYPE_ARRAY) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_recurse(iter, &dict); > + > + ? ? ? while ((ctype = dbus_message_iter_get_arg_type(&dict)) != > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID) { > + ? ? ? ? ? ? ? DBusMessageIter entry; > + ? ? ? ? ? ? ? const char *key; > + ? ? ? ? ? ? ? struct metadata_value *value; > + ? ? ? ? ? ? ? int id; > + > + ? ? ? ? ? ? ? if (ctype != DBUS_TYPE_DICT_ENTRY) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_recurse(&dict, &entry); > + ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_get_basic(&entry, &key); > + ? ? ? ? ? ? ? dbus_message_iter_next(&entry); > + > + ? ? ? ? ? ? ? id = metadata_to_val(key); > + ? ? ? ? ? ? ? if (id < 0) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_recurse(&entry, &var); > + > + ? ? ? ? ? ? ? value = g_new0(struct metadata_value, 1); > + ? ? ? ? ? ? ? value->type = dbus_message_iter_get_arg_type(&var); > + > + ? ? ? ? ? ? ? switch (id) { > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TITLE: > + ? ? ? ? ? ? ? ? ? ? ? title = TRUE; > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ARTIST: > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_ALBUM: > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_GENRE: > + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_STRING) { > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value); > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + ? ? ? ? ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.str); > + ? ? ? ? ? ? ? ? ? ? ? break; > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_TRACK: > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: > + ? ? ? ? ? ? ? case AVRCP_MEDIA_ATTRIBUTE_DURATION: > + ? ? ? ? ? ? ? ? ? ? ? if (value->type != DBUS_TYPE_UINT32) { > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_free(value); > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + ? ? ? ? ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? ? ? ? ? dbus_message_iter_get_basic(&var, &value->value.num); > + ? ? ? ? ? ? ? ? ? ? ? break; > + ? ? ? ? ? ? ? default: > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? switch (value->type) { > + ? ? ? ? ? ? ? case DBUS_TYPE_STRING: > + ? ? ? ? ? ? ? ? ? ? ? value->value.str = g_strdup(value->value.str); > + ? ? ? ? ? ? ? ? ? ? ? DBG("%s=%s", key, value->value.str); > + ? ? ? ? ? ? ? ? ? ? ? break; > + ? ? ? ? ? ? ? default: > + ? ? ? ? ? ? ? ? ? ? ? DBG("%s=%u", key, value->value.num); > + ? ? ? ? ? ? ? } > + > + ? ? ? ? ? ? ? if (!mp->track) > + ? ? ? ? ? ? ? ? ? ? ? mp->track = g_hash_table_new_full(g_direct_hash, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? g_direct_equal, NULL, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? metadata_value_free); > + > + ? ? ? ? ? ? ? g_hash_table_replace(mp->track, GUINT_TO_POINTER(id), value); > + ? ? ? ? ? ? ? dbus_message_iter_next(&dict); > + ? ? ? } > + > + ? ? ? if (title == FALSE) > + ? ? ? ? ? ? ? return TRUE; return FALSE; > + > + ? ? ? mp->position = 0; > + ? ? ? g_timer_start(mp->timer); > + > + ? ? ? avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, NULL); > + > + ? ? ? return TRUE; > +} > + > +static gboolean track_changed(DBusConnection *connection, DBusMessage *msg, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *user_data) > +{ > + ? ? ? struct media_player *mp = user_data; > + ? ? ? DBusMessageIter iter; > + > + ? ? ? DBG("sender=%s path=%s", mp->sender, mp->path); > + > + ? ? ? dbus_message_iter_init(msg, &iter); > + > + ? ? ? if (parse_player_metadata(mp, &iter) == FALSE) { > + ? ? ? ? ? ? ? error("Unexpected signature in %s.%s signal", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dbus_message_get_interface(msg), > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dbus_message_get_member(msg)); > + ? ? ? } > + > + ? ? ? return TRUE; > +} > + > +static struct media_player *media_player_create(struct media_adapter *adapter, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *sender, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *path, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int *err) > +{ > + ? ? ? struct media_player *mp; > + > + ? ? ? mp = g_new0(struct media_player, 1); > + ? ? ? mp->adapter = adapter; > + ? ? ? mp->sender = g_strdup(sender); > + ? ? ? mp->path = g_strdup(path); > + ? ? ? mp->timer = g_timer_new(); > + > + ? ? ? mp->watch = g_dbus_add_disconnect_watch(adapter->conn, sender, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? media_player_exit, mp, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NULL); > + ? ? ? mp->property_watch = g_dbus_add_signal_watch(adapter->conn, sender, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? path, MEDIA_PLAYER_INTERFACE, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "PropertyChanged", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? property_changed, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mp, NULL); > + ? ? ? mp->track_watch = g_dbus_add_signal_watch(adapter->conn, sender, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? path, MEDIA_PLAYER_INTERFACE, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "TrackChanged", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? track_changed, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mp, NULL); > + ? ? ? mp->player = avrcp_register_player(&adapter->src, &player_cb, mp, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? media_player_free); > + ? ? ? if (!mp->player) { > + ? ? ? ? ? ? ? if (err) > + ? ? ? ? ? ? ? ? ? ? ? *err = -EPROTONOSUPPORT; > + ? ? ? ? ? ? ? media_player_destroy(mp); > + ? ? ? ? ? ? ? return NULL; > + ? ? ? } > + > + ? ? ? adapter->players = g_slist_append(adapter->players, mp); > + > + ? ? ? info("Player registered: sender=%s path=%s", sender, path); > + > + ? ? ? if (err) > + ? ? ? ? ? ? ? *err = 0; > + > + ? ? ? return mp; > +} > + > +static gboolean parse_player_properties(struct media_player *mp, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBusMessageIter *iter) > +{ > + ? ? ? DBusMessageIter dict; > + ? ? ? int ctype; > + > + ? ? ? ctype = dbus_message_iter_get_arg_type(iter); > + ? ? ? if (ctype != DBUS_TYPE_ARRAY) > + ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? dbus_message_iter_recurse(iter, &dict); > + > + ? ? ? while ((ctype = dbus_message_iter_get_arg_type(&dict)) != > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID) { > + ? ? ? ? ? ? ? DBusMessageIter entry; > + ? ? ? ? ? ? ? const char *key; > + > + ? ? ? ? ? ? ? if (ctype != DBUS_TYPE_DICT_ENTRY) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_recurse(&dict, &entry); > + ? ? ? ? ? ? ? if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_get_basic(&entry, &key); > + ? ? ? ? ? ? ? dbus_message_iter_next(&entry); > + > + ? ? ? ? ? ? ? if (set_property(mp, key, &entry) == FALSE) > + ? ? ? ? ? ? ? ? ? ? ? return FALSE; > + > + ? ? ? ? ? ? ? dbus_message_iter_next(&dict); > + ? ? ? } > + > + ? ? ? return TRUE; > +} > + > +static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *data) > +{ > + ? ? ? struct media_adapter *adapter = data; > + ? ? ? struct media_player *mp; > + ? ? ? DBusMessageIter args; > + ? ? ? const char *sender, *path; > + ? ? ? int err; > + > + ? ? ? sender = dbus_message_get_sender(msg); > + > + ? ? ? dbus_message_iter_init(msg, &args); > + > + ? ? ? dbus_message_iter_get_basic(&args, &path); > + ? ? ? dbus_message_iter_next(&args); > + > + ? ? ? if (media_adapter_find_player(adapter, sender, path) != NULL) > + ? ? ? ? ? ? ? return btd_error_already_exists(msg); > + > + ? ? ? mp = media_player_create(adapter, sender, path, &err); > + ? ? ? if (mp == NULL) { > + ? ? ? ? ? ? ? if (err == -EPROTONOSUPPORT) > + ? ? ? ? ? ? ? ? ? ? ? return btd_error_not_supported(msg); > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > + ? ? ? } > + > + ? ? ? if (parse_player_properties(mp, &args) == FALSE) { > + ? ? ? ? ? ? ? media_player_destroy(mp); > + ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > + ? ? ? } > + > + ? ? ? dbus_message_iter_next(&args); > + > + ? ? ? if (parse_player_metadata(mp, &args) == FALSE) { > + ? ? ? ? ? ? ? media_player_destroy(mp); > + ? ? ? ? ? ? ? return btd_error_invalid_args(msg); > + ? ? ? } > + > + ? ? ? return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); > +} > + > +static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *data) > +{ > + ? ? ? struct media_adapter *adapter = data; > + ? ? ? struct media_player *player; > + ? ? ? const char *sender, *path; > + > + ? ? ? if (!dbus_message_get_args(msg, NULL, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_OBJECT_PATH, &path, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID)) > + ? ? ? ? ? ? ? return NULL; > + > + ? ? ? sender = dbus_message_get_sender(msg); > + > + ? ? ? player = media_adapter_find_player(adapter, sender, path); > + ? ? ? if (player == NULL) > + ? ? ? ? ? ? ? return btd_error_does_not_exist(msg); > + > + ? ? ? media_player_remove(player); > + > + ? ? ? return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); > +} > + > ?static GDBusMethodTable media_methods[] = { > ? ? ? ?{ "RegisterEndpoint", ? "oa{sv}", ? ? ? "", ? ? register_endpoint }, > ? ? ? ?{ "UnregisterEndpoint", "o", ? ? ? ? ? ?"", ? ? unregister_endpoint }, > + ? ? ? { "RegisterPlayer", ? ? "oa{sv}a{sv}","", ? ? ? register_player }, > + ? ? ? { "UnregisterPlayer", ? "o", ? ? ? ? ? ?"", ? ? unregister_player }, > ? ? ? ?{ }, > ?}; > > diff --git a/doc/media-api.txt b/doc/media-api.txt > index b2f239a..af4cfa0 100644 > --- a/doc/media-api.txt > +++ b/doc/media-api.txt > @@ -44,6 +44,169 @@ Methods ? ? ? ? ? ? void RegisterEndpoint(object endpoint, dict properties) > > ? ? ? ? ? ? ? ? ? ? ? ?Unregister sender end point. > > + ? ? ? ? ? ? ? void RegisterPlayer(object player, dict properties, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dict metadata) > + > + ? ? ? ? ? ? ? ? ? ? ? Register a media player object to sender, the sender > + ? ? ? ? ? ? ? ? ? ? ? can register as many objets as it likes. > + > + ? ? ? ? ? ? ? ? ? ? ? Note: If the sender disconnects its objects are > + ? ? ? ? ? ? ? ? ? ? ? automatically unregistered. > + > + ? ? ? ? ? ? ? ? ? ? ? Properties: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Equalizer: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Possible values: "off" or "on" > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Repeat: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "singletrack", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "alltracks" or "group" > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Shuffle: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "alltracks" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "group" > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Scan: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "alltracks" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "group" > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Status: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Possible values: "playing", "stopped", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "paused", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "forward-seek", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "reverse-seek" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "error" > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 Position > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Playback position in milliseconds > + > + ? ? ? ? ? ? ? ? ? ? ? Metadata: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Title: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track title name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Artist: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track artist name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Album: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track album name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Genre: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track genre name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 NumberOfTracks: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Number of tracks in total > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 Number: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track number > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 Duration: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track duration in milliseconds > + > + ? ? ? ? ? ? ? ? ? ? ? Possible Errors: org.bluez.Error.InvalidArguments > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?org.bluez.Error.NotSupported > + > + ? ? ? ? ? ? ? void UnregisterPlayer(object player) > + > + ? ? ? ? ? ? ? ? ? ? ? Unregister sender media player. > + > +MediaPlayer hierarchy > +===================== > + > +Service ? ? ? ? ? ? ? ?unique name > +Interface ? ? ?org.bluez.MediaPlayer > +Object path ? ?freely definable > + > +Methods ? ? ? ? ? ? ? ?void SetProperty(string property, variant value) > + > + ? ? ? ? ? ? ? ? ? ? ? Changes the value of the specified property. Only > + ? ? ? ? ? ? ? ? ? ? ? properties that are listed a read-write can be changed. > + > + ? ? ? ? ? ? ? ? ? ? ? On success this will emit a PropertyChanged signal. > + > +Signals ? ? ? ? ? ? ? ?PropertyChanged(string setting, variant value) > + > + ? ? ? ? ? ? ? ? ? ? ? This signal indicates a changed value of the given > + ? ? ? ? ? ? ? ? ? ? ? property. > + > + ? ? ? ? ? ? ? TrackChanged(dict metadata) > + > + ? ? ? ? ? ? ? ? ? ? ? Possible values: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Title: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track title name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Artist: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track artist name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Album: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track album name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string Genre: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track genre name > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 NumberOfTracks: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Number of tracks in total > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 Number: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track number > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint32 Duration: > + > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Track duration in milliseconds > + > + ? ? ? ? ? ? ? StatusChanged(string status, uint32 position) > + > + ? ? ? ? ? ? ? ? ? ? ? Possible status: "playing", "stopped", "paused", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "forward-seek", "reverse-seek" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "error" > + > +Properties ? ? string Equalizer [readwrite] > + > + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off" or "on" > + > + ? ? ? ? ? ? ? string Repeat [readwrite] > + > + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "singletrack", "alltracks" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "group" > + > + ? ? ? ? ? ? ? string Shuffle [readwrite] > + > + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "alltracks" or "group" > + > + ? ? ? ? ? ? ? string Scan [readwrite] > + > + ? ? ? ? ? ? ? ? ? ? ? Possible values: "off", "alltracks" or "group" > + > + ? ? ? ? ? ? ? string Status [readonly] > + > + ? ? ? ? ? ? ? ? ? ? ? Possible status: "playing", "stopped", "paused", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "forward-seek", "reverse-seek" or > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "error" > + > + ? ? ? ? ? ? ? uint32 Position [readonly] > + > + ? ? ? ? ? ? ? ? ? ? ? Playback position in milliseconds > + > ?MediaEndpoint hierarchy > ?======================= The rest looks good. Lucas De Marchi