As per AVRCP 1.5 spec, 6.7.2, page 57. EVENT_PLAYBACK_POS_CHANGED shall be notified in the following conditions: 1. TG has reached the registered playback Interval time. 2. Changed PLAY STATUS. 3. Changed Current Track. 4. Reached end or beginning of track. --- profiles/audio/avrcp.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ profiles/audio/avrcp.h | 1 + profiles/audio/media.c | 23 ++++++++++++++--------- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index 5c3c4f9..77f1dcb 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -198,6 +198,7 @@ struct pending_list_items { struct avrcp_player { struct avrcp_server *server; GSList *sessions; + uint16_t pos_timer; uint16_t id; uint8_t scope; uint64_t uid; @@ -627,6 +628,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, uint8_t buf[AVRCP_HEADER_LENGTH + 9]; struct avrcp_header *pdu = (void *) buf; uint16_t size; + uint32_t *pos = NULL; GSList *l; int attr; int val; @@ -673,6 +675,18 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, pdu->params[size++] = attr; pdu->params[size++] = val; break; + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + size = 5; + pos = (uint32_t *) data; + + be32toh(*pos); + memcpy(&pdu->params[1], pos, sizeof(uint32_t)); + + if (player->pos_timer > 0) { + g_source_remove(player->pos_timer); + player->pos_timer = 0; + } + break; default: error("Unknown event %u", id); return; @@ -1429,6 +1443,19 @@ static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed, return handler->func(session); } +static gboolean interval_timeout(gpointer user_data) +{ + uint32_t pos; + struct avrcp_player *mp = user_data; + + pos = player_get_position(mp); + mp->pos_timer = 0; + avrcp_player_event(mp, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &pos); + + return FALSE; +} + static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) @@ -1436,6 +1463,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct avrcp_player *player = target_get_player(session); struct btd_device *dev = session->dev; uint16_t len = ntohs(pdu->params_len); + uint32_t interval; + uint32_t pos; uint64_t uid; GList *settings; @@ -1467,6 +1496,20 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, case AVRCP_EVENT_TRACK_REACHED_START: len = 1; break; + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + len = 5; + + interval = htole32(pdu->params[1]); + player->pos_timer = g_timeout_add_seconds(interval, + interval_timeout, player); + + pos = player_get_position(player); + be32toh(pos); + /* Fill the value for sending INTERIM response */ + memcpy(&pdu->params[1], &pos, sizeof(uint32_t)); + + break; + case AVRCP_EVENT_SETTINGS_CHANGED: len = 1; settings = player_list_settings(player); @@ -2949,6 +2992,11 @@ static void player_destroy(gpointer data) if (player->destroy) player->destroy(player->user_data); + if (player->pos_timer != 0) { + g_source_remove(player->pos_timer); + player->pos_timer = 0; + } + g_slist_free(player->sessions); g_free(player->path); g_free(player->change_path); @@ -3407,6 +3455,7 @@ static void target_init(struct avrcp *session) (1 << AVRCP_EVENT_TRACK_CHANGED) | (1 << AVRCP_EVENT_TRACK_REACHED_START) | (1 << AVRCP_EVENT_TRACK_REACHED_END) | + (1 << AVRCP_EVENT_PLAYBACK_POS_CHANGED) | (1 << AVRCP_EVENT_SETTINGS_CHANGED); if (target->version < 0x0104) diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h index 6ac5294..4816e4b 100644 --- a/profiles/audio/avrcp.h +++ b/profiles/audio/avrcp.h @@ -74,6 +74,7 @@ #define AVRCP_EVENT_TRACK_CHANGED 0x02 #define AVRCP_EVENT_TRACK_REACHED_END 0x03 #define AVRCP_EVENT_TRACK_REACHED_START 0x04 +#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 #define AVRCP_EVENT_SETTINGS_CHANGED 0x08 #define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a #define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b diff --git a/profiles/audio/media.c b/profiles/audio/media.c index ef7b910..cc19c61 100644 --- a/profiles/audio/media.c +++ b/profiles/audio/media.c @@ -1305,6 +1305,9 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) mp->status = g_strdup(value); avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status); + avrcp_player_event(mp->player, + AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &mp->position); return TRUE; } @@ -1312,7 +1315,6 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) { uint64_t value; - const char *status; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64) return FALSE; @@ -1321,11 +1323,6 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) value /= 1000; - if (value > get_position(mp)) - status = "forward-seek"; - else - status = "reverse-seek"; - mp->position = value; g_timer_start(mp->timer); @@ -1334,6 +1331,9 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) if (!mp->position) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); + avrcp_player_event(mp->player, + AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &mp->position); return TRUE; } @@ -1344,11 +1344,13 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) if (mp->position == UINT32_MAX || mp->position >= mp->duration) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END, NULL); + avrcp_player_event(mp->player, + AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &mp->position); return TRUE; } - - /* Send a status change to force resync the position */ - avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &mp->position); return TRUE; } @@ -1456,6 +1458,7 @@ static gboolean parse_player_metadata(struct media_player *mp, int ctype; gboolean title = FALSE; uint64_t uid; + uint32_t pos; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) @@ -1521,9 +1524,11 @@ static gboolean parse_player_metadata(struct media_player *mp, mp->position = 0; g_timer_start(mp->timer); uid = get_uid(mp); + pos = get_position(mp); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, &pos); return TRUE; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html