* [PATCH BlueZ 01/16] audio/player: Split item creation
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 02/16] audio/AVRCP: Add browsed flag to player Luiz Augusto von Dentz
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This split item creation into two functions, media_player_create_folder
for folders and media_player_create_item for regular items.
---
profiles/audio/avrcp.c | 4 +-
profiles/audio/player.c | 343 +++++++++++++++++++++++++++++++++---------------
profiles/audio/player.h | 11 +-
3 files changed, 250 insertions(+), 108 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index e6d0e72..3d54fbe 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2207,7 +2207,7 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
if (features[7] & 0x08) {
media_player_set_browsable(mp, true);
media_player_create_folder(mp, "/Filesystem",
- PLAYER_FOLDER_TYPE_MIXED);
+ PLAYER_FOLDER_TYPE_MIXED, 0);
}
if (features[7] & 0x10)
@@ -2215,7 +2215,7 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
if (features[8] & 0x02) {
media_player_create_folder(mp, "/NowPlaying",
- PLAYER_FOLDER_TYPE_MIXED);
+ PLAYER_FOLDER_TYPE_MIXED, 0);
media_player_set_playlist(mp, "/NowPlaying");
}
}
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index e08e373..c774cfe 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -65,11 +65,15 @@ struct media_item {
player_item_type_t type; /* Item type */
player_folder_type_t folder_type; /* Folder type */
bool playable; /* Item playable flag */
+ uint64_t uid; /* Item uid */
};
struct media_folder {
+ struct media_folder *parent;
struct media_item *item; /* Folder item */
uint32_t number_of_items;/* Number of items */
+ GSList *subfolders;
+ GSList *items;
};
struct media_player {
@@ -79,8 +83,9 @@ struct media_player {
char *subtype; /* Player subtype */
bool browsable; /* Player browsing feature */
bool searchable; /* Player searching feature */
+ struct media_folder *scope; /* Player current scope */
struct media_folder *folder; /* Player current folder */
- struct media_item *playlist; /* Player current playlist */
+ struct media_folder *playlist; /* Player current playlist */
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
GHashTable *track; /* Player current track */
@@ -375,7 +380,7 @@ static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
{
struct media_player *mp = data;
- return mp->folder != NULL;
+ return mp->scope != NULL;
}
static gboolean get_browsable(const GDBusPropertyTable *property,
@@ -384,7 +389,7 @@ static gboolean get_browsable(const GDBusPropertyTable *property,
struct media_player *mp = data;
dbus_bool_t value;
- if (mp->folder == NULL)
+ if (mp->scope == NULL)
return FALSE;
DBG("%s", mp->browsable ? "true" : "false");
@@ -401,7 +406,7 @@ static gboolean searchable_exists(const GDBusPropertyTable *property,
{
struct media_player *mp = data;
- return mp->folder != NULL;
+ return mp->scope != NULL;
}
static gboolean get_searchable(const GDBusPropertyTable *property,
@@ -434,13 +439,13 @@ static gboolean get_playlist(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct media_player *mp = data;
- struct media_item *playlist = mp->playlist;
+ struct media_folder *playlist = mp->playlist;
if (playlist == NULL)
return FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &playlist->path);
+ &playlist->item->path);
return TRUE;
}
@@ -626,6 +631,93 @@ static DBusMessage *media_folder_list_items(DBusConnection *conn,
return btd_error_failed(msg, strerror(ENOTSUP));
}
+static void media_item_free(struct media_item *item)
+{
+ g_free(item->path);
+ g_free(item->name);
+ g_free(item);
+}
+
+static void media_item_destroy(void *data)
+{
+ struct media_item *item = data;
+
+ DBG("%s", item->path);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
+ MEDIA_ITEM_INTERFACE);
+
+ media_item_free(item);
+}
+
+static void media_folder_destroy(void *data)
+{
+ struct media_folder *folder = data;
+
+ g_slist_free_full(folder->subfolders, media_folder_destroy);
+ g_slist_free_full(folder->items, media_item_destroy);
+
+ media_item_destroy(folder->item);
+ g_free(folder);
+}
+
+static void media_player_change_scope(struct media_player *mp,
+ struct media_folder *folder)
+{
+ if (mp->scope == folder)
+ return;
+
+ DBG("%s", folder->item->name);
+
+ /* Skip setting current folder if folder is current playlist */
+ if (folder == mp->playlist)
+ goto cleanup;
+
+ mp->folder = folder;
+
+ /* Skip item cleanup if scope is the current playlist */
+ if (mp->scope == mp->playlist)
+ goto done;
+
+cleanup:
+ g_slist_free_full(mp->scope->items, media_item_destroy);
+ mp->scope->items = NULL;
+
+done:
+ mp->scope = folder;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+ MEDIA_FOLDER_INTERFACE, "Name");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+ MEDIA_FOLDER_INTERFACE, "NumberOfItems");
+}
+
+static struct media_folder *find_folder(GSList *folders, const char *pattern)
+{
+ GSList *l;
+
+ for (l = folders; l; l = l->next) {
+ struct media_folder *folder = l->data;
+
+ if (g_str_equal(folder->item->name, pattern))
+ return folder;
+
+ if (g_str_equal(folder->item->path, pattern))
+ return folder;
+
+ folder = find_folder(folder->subfolders, pattern);
+ if (folder != NULL)
+ return folder;
+ }
+
+ return NULL;
+}
+
+static struct media_folder *media_player_find_folder(struct media_player *mp,
+ const char *pattern)
+{
+ return find_folder(mp->folders, pattern);
+}
static DBusMessage *media_folder_change_folder(DBusConnection *conn,
DBusMessage *msg, void *data)
@@ -637,7 +729,7 @@ static gboolean folder_name_exists(const GDBusPropertyTable *property,
void *data)
{
struct media_player *mp = data;
- struct media_folder *folder = mp->folder;
+ struct media_folder *folder = mp->scope;
if (folder == NULL || folder->item == NULL)
return FALSE;
@@ -649,7 +741,7 @@ static gboolean get_folder_name(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct media_player *mp = data;
- struct media_folder *folder = mp->folder;
+ struct media_folder *folder = mp->scope;
if (folder == NULL || folder->item == NULL)
return FALSE;
@@ -666,14 +758,14 @@ static gboolean items_exists(const GDBusPropertyTable *property, void *data)
{
struct media_player *mp = data;
- return mp->folder != NULL;
+ return mp->scope != NULL;
}
static gboolean get_items(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct media_player *mp = data;
- struct media_folder *folder = mp->folder;
+ struct media_folder *folder = mp->scope;
if (folder == NULL)
return FALSE;
@@ -709,23 +801,24 @@ static const GDBusPropertyTable media_folder_properties[] = {
{ }
};
-static void media_item_destroy(void *data)
+static void media_player_set_scope(struct media_player *mp,
+ struct media_folder *folder)
{
- struct media_item *item = data;
-
- DBG("%s", item->path);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
- MEDIA_ITEM_INTERFACE);
-
- g_free(item->path);
- g_free(item->name);
- g_free(item);
-}
+ if (mp->scope == NULL) {
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ mp->path, MEDIA_FOLDER_INTERFACE,
+ media_folder_methods,
+ NULL,
+ media_folder_properties, mp, NULL)) {
+ error("D-Bus failed to register %s on %s path",
+ MEDIA_FOLDER_INTERFACE, mp->path);
+ return;
+ }
+ mp->scope = folder;
+ return;
+ }
-static void media_folder_destroy(struct media_folder *folder)
-{
- g_free(folder);
+ return media_player_change_scope(mp, folder);
}
void media_player_destroy(struct media_player *mp)
@@ -744,15 +837,13 @@ void media_player_destroy(struct media_player *mp)
if (mp->process_id > 0)
g_source_remove(mp->process_id);
- if (mp->folder) {
+ if (mp->scope)
g_dbus_unregister_interface(btd_get_dbus_connection(),
mp->path,
MEDIA_FOLDER_INTERFACE);
- media_folder_destroy(mp->folder);
- }
g_slist_free_full(mp->pending, g_free);
- g_slist_free_full(mp->folders, media_item_destroy);
+ g_slist_free_full(mp->folders, media_folder_destroy);
g_timer_destroy(mp->progress);
g_free(mp->cb);
@@ -1006,90 +1097,58 @@ void media_player_set_searchable(struct media_player *mp, bool enabled)
"Searchable");
}
-static void media_player_set_folder_item(struct media_player *mp,
- struct media_item *item,
+void media_player_set_folder(struct media_player *mp, const char *name,
uint32_t number_of_items)
{
struct media_folder *folder;
- folder = mp->folder;
+ DBG("%s number of items %u", name, number_of_items);
+ folder = media_player_find_folder(mp, name);
if (folder == NULL) {
- folder = g_new0(struct media_folder, 1);
- mp->folder = folder;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- media_folder_methods,
- NULL,
- media_folder_properties, mp, NULL)) {
- error("D-Bus failed to register %s on %s path",
- MEDIA_FOLDER_INTERFACE, mp->path);
- media_folder_destroy(mp->folder);
- mp->folder = NULL;
- return;
- }
+ error("Unknown folder: %s", name);
+ return;
}
- folder->item = item;
folder->number_of_items = number_of_items;
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_FOLDER_INTERFACE, "Name");
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_FOLDER_INTERFACE, "NumberOfItems");
-}
-
-static struct media_item *media_player_find_folder(struct media_player *mp,
- const char *name)
-{
- GSList *l;
-
- for (l = mp->folders; l; l = l->next) {
- struct media_item *item = l->data;
-
- if (item->type != PLAYER_ITEM_TYPE_FOLDER)
- continue;
-
- if (g_str_equal(item->name, name))
- return item;
- }
-
- return NULL;
+ media_player_set_scope(mp, folder);
}
-void media_player_set_folder(struct media_player *mp, const char *name,
- uint32_t number_of_items)
+void media_player_set_playlist(struct media_player *mp, const char *name)
{
- struct media_item *item;
+ struct media_folder *folder;
- DBG("%s number of items %u", name, number_of_items);
+ DBG("%s", name);
- item = media_player_find_folder(mp, name);
- if (item == NULL) {
+ folder = media_player_find_folder(mp, name);
+ if (folder == NULL) {
error("Unknown folder: %s", name);
return;
}
- media_player_set_folder_item(mp, item, number_of_items);
+ mp->playlist = folder;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+ MEDIA_PLAYER_INTERFACE, "Playlist");
}
-void media_player_set_playlist(struct media_player *mp, const char *name)
+static struct media_item *media_folder_find_item(struct media_folder *folder,
+ uint64_t uid)
{
- struct media_item *item;
+ GSList *l;
- DBG("%s", name);
+ if (uid == 0)
+ return NULL;
- item = media_player_find_folder(mp, name);
- if (item == NULL) {
- error("Unknown folder: %s", name);
- return;
- }
+ for (l = folder->items; l; l = l->next) {
+ struct media_item *item = l->data;
- mp->playlist = item;
+ if (item->uid == uid)
+ return item;
+ }
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE, "Playlist");
+ return NULL;
}
static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
@@ -1238,22 +1297,35 @@ static const GDBusPropertyTable media_item_properties[] = {
{ }
};
-static struct media_item *media_player_create_item(struct media_player *mp,
+struct media_item *media_player_create_item(struct media_player *mp,
const char *name,
- player_item_type_t type)
+ player_item_type_t type,
+ uint64_t uid)
{
+ struct media_folder *folder = mp->scope;
struct media_item *item;
const char *strtype;
+ item = media_folder_find_item(folder, uid);
+ if (item != NULL)
+ return item;
+
strtype = type_to_string(type);
if (strtype == NULL)
return NULL;
- DBG("%s type %s", name, strtype);
+ DBG("%s type %s uid %" PRIu64 "", name, strtype, uid);
item = g_new0(struct media_item, 1);
item->player = mp;
- item->path = g_strdup_printf("%s%s", mp->path, name);
+ item->uid = uid;
+
+ if (uid > 0)
+ item->path = g_strdup_printf("%s/item%" PRIu64 "",
+ folder->item->path, uid);
+ else
+ item->path = g_strdup_printf("%s%s", mp->path, name);
+
item->name = g_strdup(name);
item->type = type;
item->folder_type = PLAYER_FOLDER_TYPE_INVALID;
@@ -1265,35 +1337,98 @@ static struct media_item *media_player_create_item(struct media_player *mp,
media_item_properties, item, NULL)) {
error("D-Bus failed to register %s on %s path",
MEDIA_ITEM_INTERFACE, item->path);
- media_item_destroy(item);
+ media_item_free(item);
return NULL;
}
+ if (type != PLAYER_ITEM_TYPE_FOLDER)
+ folder->items = g_slist_prepend(folder->items, item);
+
+ DBG("%s", item->path);
+
return item;
}
-int media_player_create_folder(struct media_player *mp, const char *name,
- player_folder_type_t type)
+static struct media_item *
+media_player_create_subfolder(struct media_player *mp, const char *name,
+ uint64_t uid)
{
+ struct media_folder *folder = mp->scope;
struct media_item *item;
+ char *path;
- item = media_player_find_folder(mp, name);
- if (item != NULL)
- return 0;
+ path = g_strdup_printf("%s/%s", folder->item->name, name);
+
+ DBG("%s", path);
+
+ item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
+ uid);
+ g_free(path);
+
+ return item;
+}
+
+static struct media_folder *
+media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
+{
+ struct media_folder *folder = mp->scope;
+ GSList *l;
+
+ for (l = folder->subfolders; l; l = l->next) {
+ struct media_folder *folder = l->data;
+
+ if (folder->item->uid == uid)
+ return folder;
+ }
+
+ return NULL;
+}
+
+struct media_item *media_player_create_folder(struct media_player *mp,
+ const char *name,
+ player_folder_type_t type,
+ uint64_t uid)
+{
+ struct media_folder *folder;
+ struct media_item *item;
+
+ if (uid > 0)
+ folder = media_player_find_folder_by_uid(mp, uid);
+ else
+ folder = media_player_find_folder(mp, name);
+
+ if (folder != NULL)
+ return folder->item;
+
+ if (uid > 0)
+ item = media_player_create_subfolder(mp, name, uid);
+ else
+ item = media_player_create_item(mp, name,
+ PLAYER_ITEM_TYPE_FOLDER, uid);
- item = media_player_create_item(mp, name,
- PLAYER_ITEM_TYPE_FOLDER);
if (item == NULL)
- return -EINVAL;
+ return NULL;
+
+ folder = g_new0(struct media_folder, 1);
+ folder->item = item;
item->folder_type = type;
- if (mp->folder == NULL)
- media_player_set_folder_item(mp, item, 0);
+ if (mp->folder != NULL)
+ goto done;
- mp->folders = g_slist_prepend(mp->folders, item);
+ mp->folder = folder;
- return 0;
+done:
+ if (uid > 0) {
+ folder->parent = mp->folder;
+ mp->folder->subfolders = g_slist_prepend(
+ mp->folder->subfolders,
+ folder);
+ } else
+ mp->folders = g_slist_prepend(mp->folders, folder);
+
+ return item;
}
void media_player_set_callbacks(struct media_player *mp,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 852042e..6e44308 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -42,6 +42,7 @@ typedef enum {
} player_folder_type_t;
struct media_player;
+struct media_item;
struct media_player_callback {
bool (*set_setting) (struct media_player *mp, const char *key,
@@ -75,8 +76,14 @@ void media_player_set_folder(struct media_player *mp, const char *path,
uint32_t items);
void media_player_set_playlist(struct media_player *mp, const char *name);
-int media_player_create_folder(struct media_player *mp, const char *name,
- player_folder_type_t type);
+struct media_item *media_player_create_folder(struct media_player *mp,
+ const char *name,
+ player_folder_type_t type,
+ uint64_t uid);
+struct media_item *media_player_create_item(struct media_player *mp,
+ const char *name,
+ player_item_type_t type,
+ uint64_t uid);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 02/16] audio/AVRCP: Add browsed flag to player
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 01/16] audio/player: Split item creation Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 03/16] audio/AVRCP: Add support for GetFolderItems command Luiz Augusto von Dentz
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This flag indicates if the player is browsed and are checked to avoid
setting the same player as browsed multiple times.
---
profiles/audio/avrcp.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 3d54fbe..90c80c3 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -182,6 +182,7 @@ struct avrcp_player {
GSList *sessions;
uint16_t id;
uint16_t uid_counter;
+ bool browsed;
uint8_t *features;
struct avrcp_player_cb *cb;
@@ -2095,6 +2096,7 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
return FALSE;
player->uid_counter = bt_get_be16(&pdu->params[1]);
+ player->browsed = true;
items = bt_get_be32(&pdu->params[3]);
@@ -2444,7 +2446,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
media_player_set_name(mp, name);
}
- if (session->player == player)
+ if (session->player == player && !player->browsed)
avrcp_set_browsed_player(session, player);
return player;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 03/16] audio/AVRCP: Add support for GetFolderItems command
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 01/16] audio/player: Split item creation Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 02/16] audio/AVRCP: Add browsed flag to player Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 04/16] audio/player: Add implementation of MediaFolder.ListItems Luiz Augusto von Dentz
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for GetFolderItems command via player callback.
---
profiles/audio/avrcp.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++-
profiles/audio/player.h | 2 +
2 files changed, 195 insertions(+), 4 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 90c80c3..1da9541 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -177,14 +177,24 @@ struct pending_pdu {
uint16_t offset;
};
+struct pending_list_items {
+ GSList *items;
+ uint32_t start;
+ uint32_t end;
+};
+
struct avrcp_player {
struct avrcp_server *server;
GSList *sessions;
uint16_t id;
+ uint8_t scope;
+ uint64_t uid;
uint16_t uid_counter;
bool browsed;
uint8_t *features;
+ struct pending_list_items *p;
+
struct avrcp_player_cb *cb;
void *user_data;
GDestroyNotify destroy;
@@ -2077,6 +2087,158 @@ static const char *subtype_to_string(uint32_t subtype)
return "None";
}
+static struct media_item *parse_media_element(struct avrcp *session,
+ uint8_t *operands, uint16_t len)
+{
+ struct avrcp_player *player;
+ struct media_player *mp;
+ uint16_t namelen;
+ char name[255];
+ uint64_t uid;
+
+ if (len < 13)
+ return NULL;
+
+ uid = bt_get_be64(&operands[0]);
+
+ namelen = MIN(bt_get_be16(&operands[11]), sizeof(name) - 1);
+ if (namelen > 0) {
+ memcpy(name, &operands[13], namelen);
+ name[namelen] = '\0';
+ }
+
+ player = session->player;
+ mp = player->user_data;
+
+ return media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
+}
+
+static struct media_item *parse_media_folder(struct avrcp *session,
+ uint8_t *operands, uint16_t len)
+{
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ uint16_t namelen;
+ char name[255];
+ uint64_t uid;
+ uint8_t type;
+
+ if (len < 12)
+ return NULL;
+
+ uid = bt_get_be64(&operands[0]);
+ type = operands[9];
+
+ namelen = MIN(bt_get_be16(&operands[12]), sizeof(name) - 1);
+ if (namelen > 0) {
+ memcpy(name, &operands[14], namelen);
+ name[namelen] = '\0';
+ }
+
+ return media_player_create_folder(mp, name, type, uid);
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+ uint32_t end);
+static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp_browsing_header *pdu = (void *) operands;
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct pending_list_items *p = player->p;
+ uint16_t count;
+ uint32_t items, total;
+ size_t i;
+
+ if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5)
+ goto done;
+
+ count = bt_get_be16(&operands[6]);
+ if (count == 0)
+ goto done;
+
+ for (i = 8; count && i + 3 < operand_count; count--) {
+ struct media_item *item;
+ uint8_t type;
+ uint16_t len;
+
+ type = operands[i++];
+ len = bt_get_be16(&operands[i]);
+ i += 2;
+
+ if (type != 0x03 && type != 0x02) {
+ i += len;
+ continue;
+ }
+
+ if (i + len > operand_count) {
+ error("Invalid item length");
+ break;
+ }
+
+ if (type == 0x03)
+ item = parse_media_element(session, &operands[i], len);
+ else if (type == 0x02)
+ item = parse_media_folder(session, &operands[i], len);
+
+ if (item) {
+ if (g_slist_find(p->items, item))
+ goto done;
+ p->items = g_slist_append(p->items, item);
+ }
+
+ i += len;
+ }
+
+ items = g_slist_length(p->items);
+ total = p->end - p->start;
+ if (items < total) {
+ avrcp_list_items(session, p->start + items + 1, p->end);
+ return FALSE;
+ }
+
+done:
+ g_slist_free(p->items);
+ g_free(p);
+ player->p = NULL;
+
+ return FALSE;
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+ uint32_t end)
+{
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10 +
+ AVRCP_MEDIA_ATTRIBUTE_LAST * sizeof(uint32_t)];
+ struct avrcp_player *player = session->player;
+ struct avrcp_browsing_header *pdu = (void *) buf;
+ uint16_t length = AVRCP_BROWSING_HEADER_LENGTH + 10;
+ uint32_t attribute;
+
+ memset(buf, 0, sizeof(buf));
+
+ pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+ pdu->param_len = htons(10 + sizeof(uint32_t));
+
+ pdu->params[0] = player->scope;
+
+ bt_put_be32(start, &pdu->params[1]);
+ bt_put_be32(end, &pdu->params[5]);
+
+ pdu->params[9] = 1;
+
+ /* Only the title (0x01) is mandatory. This can be extended to
+ * support AVRCP_MEDIA_ATTRIBUTE_* attributes */
+ attribute = htonl(AVRCP_MEDIA_ATTRIBUTE_TITLE);
+ memcpy(&pdu->params[10], &attribute, sizeof(uint32_t));
+
+ length += sizeof(uint32_t);
+
+ avctp_send_browsing_req(session->conn, buf, length,
+ avrcp_list_items_rsp, session);
+}
+
static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
uint8_t *operands,
size_t operand_count,
@@ -2339,6 +2501,33 @@ static int ct_rewind(struct media_player *mp, void *user_data)
return ct_press(player, AVC_REWIND);
}
+static int ct_list_items(struct media_player *mp, const char *name,
+ uint32_t start, uint32_t end, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+ struct pending_list_items *p;
+
+ if (player->p != NULL)
+ return -EBUSY;
+
+ session = player->sessions->data;
+
+ if (g_str_has_prefix(name, "/NowPlaying"))
+ player->scope = 0x03;
+ else
+ player->scope = 0x01;
+
+ avrcp_list_items(session, start, end);
+
+ p = g_new0(struct pending_list_items, 1);
+ p->start = start;
+ p->end = end;
+ player->p = p;
+
+ return 0;
+}
+
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
@@ -2348,6 +2537,7 @@ static const struct media_player_callback ct_cbs = {
.previous = ct_previous,
.fast_forward = ct_fast_forward,
.rewind = ct_rewind,
+ .list_items = ct_list_items,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
@@ -2582,11 +2772,10 @@ static void avrcp_status_changed(struct avrcp *session,
static void avrcp_track_changed(struct avrcp *session,
struct avrcp_header *pdu)
{
- uint64_t uid;
-
if (session->browsing_id) {
- uid = bt_get_be64(&pdu->params[1]);
- avrcp_get_item_attributes(session, uid);
+ struct avrcp_player *player = session->player;
+ player->uid = bt_get_be64(&pdu->params[1]);
+ avrcp_get_item_attributes(session, player->uid);
} else
avrcp_get_element_attributes(session);
}
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 6e44308..e7a885e 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -54,6 +54,8 @@ struct media_player_callback {
int (*previous) (struct media_player *mp, void *user_data);
int (*fast_forward) (struct media_player *mp, void *user_data);
int (*rewind) (struct media_player *mp, void *user_data);
+ int (*list_items) (struct media_player *mp, const char *name,
+ uint32_t start, uint32_t end, void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 04/16] audio/player: Add implementation of MediaFolder.ListItems
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (2 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 03/16] audio/AVRCP: Add support for GetFolderItems command Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 05/16] audio/player: Add support for setting current Item object Luiz Augusto von Dentz
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 21 ++++++-
profiles/audio/player.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++-
profiles/audio/player.h | 3 +
3 files changed, 164 insertions(+), 3 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 1da9541..cbac603 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -73,6 +73,7 @@
#define AVRCP_STATUS_PARAM_NOT_FOUND 0x02
#define AVRCP_STATUS_INTERNAL_ERROR 0x03
#define AVRCP_STATUS_SUCCESS 0x04
+#define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b
#define AVRCP_STATUS_INVALID_PLAYER_ID 0x11
#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12
#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15
@@ -2150,10 +2151,26 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
uint16_t count;
uint32_t items, total;
size_t i;
+ int err = 0;
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5)
+ if (pdu == NULL) {
+ err = -ETIMEDOUT;
+ goto done;
+ }
+
+ /* AVRCP 1.5 - Page 76:
+ * If the TG receives a GetFolderItems command for an empty folder then
+ * the TG shall return the error (= Range Out of Bounds) in the status
+ * field of the GetFolderItems response.
+ */
+ if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
goto done;
+ if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5) {
+ err = -EINVAL;
+ goto done;
+ }
+
count = bt_get_be16(&operands[6]);
if (count == 0)
goto done;
@@ -2199,6 +2216,8 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
}
done:
+ media_player_list_complete(player->user_data, p->items, err);
+
g_slist_free(p->items);
g_free(p);
player->p = NULL;
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index c774cfe..9bd8b26 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -74,6 +74,7 @@ struct media_folder {
uint32_t number_of_items;/* Number of items */
GSList *subfolders;
GSList *items;
+ DBusMessage *msg;
};
struct media_player {
@@ -569,6 +570,63 @@ static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+static void parse_folder_list(gpointer data, gpointer user_data)
+{
+ struct media_item *item = data;
+ DBusMessageIter *array = user_data;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+ &item->path);
+
+ g_dbus_get_properties(btd_get_dbus_connection(), item->path,
+ MEDIA_ITEM_INTERFACE, &entry);
+
+ dbus_message_iter_close_container(array, &entry);
+}
+
+void media_player_list_complete(struct media_player *mp, GSList *items,
+ int err)
+{
+ struct media_folder *folder = mp->scope;
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ if (folder == NULL || folder->msg == NULL)
+ return;
+
+ if (err < 0) {
+ reply = btd_error_failed(folder->msg, strerror(-err));
+ goto done;
+ }
+
+ reply = dbus_message_new_method_return(folder->msg);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_OBJECT_PATH_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &array);
+
+ g_slist_foreach(items, parse_folder_list, &array);
+ dbus_message_iter_close_container(&iter, &array);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(folder->msg);
+ folder->msg = NULL;
+}
+
static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL, media_player_play) },
{ GDBUS_EXPERIMENTAL_METHOD("Pause", NULL, NULL, media_player_pause) },
@@ -625,10 +683,88 @@ static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
return btd_error_failed(msg, strerror(ENOTSUP));
}
+static int parse_filters(struct media_player *player, DBusMessageIter *iter,
+ uint32_t *start, uint32_t *end)
+{
+ struct media_folder *folder = player->scope;
+ DBusMessageIter dict;
+ int ctype;
+
+ *start = 0;
+ *end = folder->number_of_items ? folder->number_of_items : UINT32_MAX;
+
+ 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, var;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&entry, &var);
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32)
+ return -EINVAL;
+
+ if (strcasecmp(key, "Start") == 0)
+ dbus_message_iter_get_basic(&var, start);
+ else if (strcasecmp(key, "End") == 0)
+ dbus_message_iter_get_basic(&var, end);
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (folder->number_of_items > 0 && *end > folder->number_of_items)
+ *end = folder->number_of_items;
+
+ return 0;
+}
+
static DBusMessage *media_folder_list_items(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return btd_error_failed(msg, strerror(ENOTSUP));
+ struct media_player *mp = data;
+ struct media_folder *folder = mp->scope;
+ struct player_callback *cb = mp->cb;
+ DBusMessageIter iter;
+ uint32_t start, end;
+ int err;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_filters(mp, &iter, &start, &end) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (cb->cbs->list_items == NULL)
+ return btd_error_not_supported(msg);
+
+ if (folder->msg != NULL)
+ return btd_error_failed(msg, strerror(EBUSY));
+
+ err = cb->cbs->list_items(mp, folder->item->name, start, end,
+ cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ folder->msg = dbus_message_ref(msg);
+
+ return NULL;
}
static void media_item_free(struct media_item *item)
@@ -657,6 +793,9 @@ static void media_folder_destroy(void *data)
g_slist_free_full(folder->subfolders, media_folder_destroy);
g_slist_free_full(folder->items, media_item_destroy);
+ if (folder->msg != NULL)
+ dbus_message_unref(folder->msg);
+
media_item_destroy(folder->item);
g_free(folder);
}
@@ -783,7 +922,7 @@ static const GDBusMethodTable media_folder_methods[] = {
GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
GDBUS_ARGS({ "folder", "o" }),
media_folder_search) },
- { GDBUS_EXPERIMENTAL_METHOD("ListItems",
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ListItems",
GDBUS_ARGS({ "filter", "a{sv}" }),
GDBUS_ARGS({ "items", "a{oa{sv}}" }),
media_folder_list_items) },
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index e7a885e..4626a24 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -87,6 +87,9 @@ struct media_item *media_player_create_item(struct media_player *mp,
player_item_type_t type,
uint64_t uid);
+void media_player_list_complete(struct media_player *mp, GSList *items,
+ int err);
+
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
void *user_data);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 05/16] audio/player: Add support for setting current Item object
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (3 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 04/16] audio/player: Add implementation of MediaFolder.ListItems Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 06/16] audio/player: Add function media_item_set_playable Luiz Augusto von Dentz
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This introduces media_player_set_playlist_item which is used when the
current item UID is known, with this the application can tell what
is the current item in the playlist.
---
profiles/audio/avrcp.c | 3 +++
profiles/audio/player.c | 42 ++++++++++++++++++++++++++++++++++++------
profiles/audio/player.h | 1 +
3 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index cbac603..539684d 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1961,6 +1961,9 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player,
struct media_player *mp = player->user_data;
int i;
+ if (player->uid > 0)
+ media_player_set_playlist_item(mp, player->uid);
+
for (i = 0; count > 0; count--) {
uint32_t id;
uint16_t charset, len;
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 9bd8b26..c151560 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -102,16 +102,18 @@ struct media_player {
static void append_metadata(void *key, void *value, void *user_data)
{
DBusMessageIter *dict = user_data;
+ const char *strkey = key;
- if (strcasecmp((char *) key, "Duration") == 0 ||
- strcasecmp((char *) key, "TrackNumber") == 0 ||
- strcasecmp((char *) key, "NumberOfTracks") == 0) {
+ if (strcasecmp(strkey, "Duration") == 0 ||
+ strcasecmp(strkey, "TrackNumber") == 0 ||
+ strcasecmp(strkey, "NumberOfTracks") == 0) {
uint32_t num = atoi(value);
dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
- return;
+ } else if (strcasecmp(strkey, "Item") == 0) {
+ dict_append_entry(dict, key, DBUS_TYPE_OBJECT_PATH, &value);
+ } else {
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
}
-
- dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
}
static struct pending_req *find_pending(struct media_player *mp,
@@ -1290,6 +1292,34 @@ static struct media_item *media_folder_find_item(struct media_folder *folder,
return NULL;
}
+void media_player_set_playlist_item(struct media_player *mp, uint64_t uid)
+{
+ struct media_folder *folder = mp->playlist;
+ struct media_item *item;
+
+ DBG("%" PRIu64 "", uid);
+
+ if (folder == NULL)
+ return;
+
+ item = media_folder_find_item(folder, uid);
+ if (item == NULL) {
+ warn("Item not found");
+ return;
+ }
+
+ if (item == g_hash_table_lookup(mp->track, "Item"))
+ return;
+
+ if (mp->process_id == 0) {
+ g_hash_table_remove_all(mp->track);
+ mp->process_id = g_idle_add(process_metadata_changed, mp);
+ }
+
+ g_hash_table_replace(mp->track, g_strdup("Item"),
+ g_strdup(item->path));
+}
+
static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
void *data)
{
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 4626a24..3a388d2 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -77,6 +77,7 @@ void media_player_set_searchable(struct media_player *mp, bool enabled);
void media_player_set_folder(struct media_player *mp, const char *path,
uint32_t items);
void media_player_set_playlist(struct media_player *mp, const char *name);
+void media_player_set_playlist_item(struct media_player *mp, uint64_t uid);
struct media_item *media_player_create_folder(struct media_player *mp,
const char *name,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 06/16] audio/player: Add function media_item_set_playable
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (4 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 05/16] audio/player: Add support for setting current Item object Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 07/16] audio/player: Add support for MediaItem.Metadata property Luiz Augusto von Dentz
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 9 ++++++++-
profiles/audio/player.c | 11 +++++++++++
profiles/audio/player.h | 1 +
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 539684d..d46e598 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2096,6 +2096,7 @@ static struct media_item *parse_media_element(struct avrcp *session,
{
struct avrcp_player *player;
struct media_player *mp;
+ struct media_item *item;
uint16_t namelen;
char name[255];
uint64_t uid;
@@ -2114,7 +2115,13 @@ static struct media_item *parse_media_element(struct avrcp *session,
player = session->player;
mp = player->user_data;
- return media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
+ item = media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
+ if (item == NULL)
+ return NULL;
+
+ media_item_set_playable(item, true);
+
+ return item;
}
static struct media_item *parse_media_folder(struct avrcp *session,
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index c151560..69e8cd5 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1466,6 +1466,17 @@ static const GDBusPropertyTable media_item_properties[] = {
{ }
};
+void media_item_set_playable(struct media_item *item, bool value)
+{
+ if (item->playable == value)
+ return;
+
+ item->playable = value;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), item->path,
+ MEDIA_ITEM_INTERFACE, "Playable");
+}
+
struct media_item *media_player_create_item(struct media_player *mp,
const char *name,
player_item_type_t type,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 3a388d2..20de0b8 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -88,6 +88,7 @@ struct media_item *media_player_create_item(struct media_player *mp,
player_item_type_t type,
uint64_t uid);
+void media_item_set_playable(struct media_item *item, bool value);
void media_player_list_complete(struct media_player *mp, GSList *items,
int err);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 07/16] audio/player: Add support for MediaItem.Metadata property
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (5 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 06/16] audio/player: Add function media_item_set_playable Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 08/16] audio/AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/player.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 69e8cd5..d56640f 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -66,6 +66,7 @@ struct media_item {
player_folder_type_t folder_type; /* Folder type */
bool playable; /* Item playable flag */
uint64_t uid; /* Item uid */
+ GHashTable *metadata; /* Item metadata */
};
struct media_folder {
@@ -771,6 +772,9 @@ static DBusMessage *media_folder_list_items(DBusConnection *conn,
static void media_item_free(struct media_item *item)
{
+ if (item->metadata != NULL)
+ g_hash_table_unref(item->metadata);
+
g_free(item->path);
g_free(item->name);
g_free(item);
@@ -1446,6 +1450,37 @@ static gboolean get_folder_type(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean metadata_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_item *item = data;
+
+ return item->metadata != NULL;
+}
+
+static gboolean get_metadata(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_item *item = data;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ if (g_hash_table_size(item->metadata) > 0)
+ g_hash_table_foreach(item->metadata, append_metadata, &dict);
+ else if (item->name != NULL)
+ dict_append_entry(&dict, "Title", DBUS_TYPE_STRING,
+ &item->name);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return TRUE;
+}
+
static const GDBusMethodTable media_item_methods[] = {
{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL,
media_item_play) },
@@ -1463,6 +1498,8 @@ static const GDBusPropertyTable media_item_properties[] = {
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Playable", "b", get_playable, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Metadata", "a{sv}", get_metadata, NULL, metadata_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
};
@@ -1521,8 +1558,11 @@ struct media_item *media_player_create_item(struct media_player *mp,
return NULL;
}
- if (type != PLAYER_ITEM_TYPE_FOLDER)
+ if (type != PLAYER_ITEM_TYPE_FOLDER) {
folder->items = g_slist_prepend(folder->items, item);
+ item->metadata = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ }
DBG("%s", item->path);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 08/16] audio/AVRCP: Add support for ChangePath command
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (6 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 07/16] audio/player: Add support for MediaItem.Metadata property Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 09/16] audio/player: Add implementation of MediaFolder.ChangeFolder Luiz Augusto von Dentz
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for ChangePath command via player callback.
---
profiles/audio/avrcp.c | 77 +++++++++++++++++++++++++++++++++++++++++++++----
profiles/audio/player.h | 2 ++
2 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index d46e598..6135d78 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -102,8 +102,9 @@
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
#define AVRCP_SET_BROWSED_PLAYER 0x70
-#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_GET_FOLDER_ITEMS 0x71
+#define AVRCP_CHANGE_PATH 0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_GENERAL_REJECT 0xA0
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
@@ -193,8 +194,10 @@ struct avrcp_player {
uint16_t uid_counter;
bool browsed;
uint8_t *features;
+ char *path;
struct pending_list_items *p;
+ char *change_path;
struct avrcp_player_cb *cb;
void *user_data;
@@ -2268,6 +2271,32 @@ static void avrcp_list_items(struct avrcp *session, uint32_t start,
avrcp_list_items_rsp, session);
}
+static gboolean avrcp_change_path_rsp(struct avctp *conn,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_browsing_header *pdu = (void *) operands;
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ uint8_t status;
+ uint32_t num_of_items;
+
+ status = pdu->params[0];
+ if (status != AVRCP_STATUS_SUCCESS)
+ return FALSE;
+
+ num_of_items = bt_get_be32(&pdu->params[1]);
+
+ g_free(player->path);
+ player->path = player->change_path;
+ player->change_path = NULL;
+
+ media_player_set_folder(mp, player->path, num_of_items);
+
+ return FALSE;
+}
+
static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
uint8_t *operands,
size_t operand_count,
@@ -2278,7 +2307,7 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
struct media_player *mp = player->user_data;
struct avrcp_browsing_header *pdu = (void *) operands;
uint32_t items;
- char **folders, *path;
+ char **folders;
uint8_t depth, count;
size_t i;
@@ -2311,12 +2340,10 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
i += len;
}
- path = g_build_pathv("/", folders);
+ player->path = g_build_pathv("/", folders);
g_strfreev(folders);
- media_player_set_folder(mp, path, items);
-
- g_free(path);
+ media_player_set_folder(mp, player->path, items);
return FALSE;
}
@@ -2557,6 +2584,41 @@ static int ct_list_items(struct media_player *mp, const char *name,
return 0;
}
+static void avrcp_change_path(struct avrcp *session, uint8_t direction,
+ uint64_t uid)
+{
+ struct avrcp_player *player = session->player;
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 11];
+ struct avrcp_browsing_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ bt_put_be16(player->uid_counter, &pdu->params[0]);
+ pdu->params[2] = direction;
+ bt_put_be64(uid, &pdu->params[3]);
+ pdu->pdu_id = AVRCP_CHANGE_PATH;
+ pdu->param_len = htons(11);
+
+ avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+ avrcp_change_path_rsp, session);
+}
+
+static int ct_change_folder(struct media_player *mp, const char *path,
+ uint64_t uid, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+ uint8_t direction;
+
+ session = player->sessions->data;
+ player->change_path = g_strdup(path);
+
+ direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
+
+ avrcp_change_path(session, direction, uid);
+
+ return 0;
+}
+
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
@@ -2567,6 +2629,7 @@ static const struct media_player_callback ct_cbs = {
.fast_forward = ct_fast_forward,
.rewind = ct_rewind,
.list_items = ct_list_items,
+ .change_folder = ct_change_folder,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
@@ -2679,6 +2742,8 @@ static void player_destroy(gpointer data)
player->destroy(player->user_data);
g_slist_free(player->sessions);
+ g_free(player->path);
+ g_free(player->change_path);
g_free(player->features);
g_free(player);
}
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 20de0b8..4103742 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -56,6 +56,8 @@ struct media_player_callback {
int (*rewind) (struct media_player *mp, void *user_data);
int (*list_items) (struct media_player *mp, const char *name,
uint32_t start, uint32_t end, void *user_data);
+ int (*change_folder) (struct media_player *mp, const char *path,
+ uint64_t uid, void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 09/16] audio/player: Add implementation of MediaFolder.ChangeFolder
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (7 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 08/16] audio/AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 10/16] audio/AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 31 ++++++++++++++++--------
profiles/audio/player.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++--
profiles/audio/player.h | 2 ++
3 files changed, 85 insertions(+), 12 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 6135d78..a35477e 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2279,20 +2279,31 @@ static gboolean avrcp_change_path_rsp(struct avctp *conn,
struct avrcp *session = user_data;
struct avrcp_player *player = session->player;
struct media_player *mp = player->user_data;
- uint8_t status;
- uint32_t num_of_items;
+ int ret;
- status = pdu->params[0];
- if (status != AVRCP_STATUS_SUCCESS)
- return FALSE;
+ if (pdu == NULL) {
+ ret = -ETIMEDOUT;
+ goto done;
+ }
- num_of_items = bt_get_be32(&pdu->params[1]);
+ if (pdu->params[0] != AVRCP_STATUS_SUCCESS) {
+ ret = -EINVAL;
+ goto done;
+ }
- g_free(player->path);
- player->path = player->change_path;
- player->change_path = NULL;
+ ret = bt_get_be32(&pdu->params[1]);
+
+done:
+ if (ret < 0) {
+ g_free(player->change_path);
+ player->change_path = NULL;
+ } else {
+ g_free(player->path);
+ player->path = player->change_path;
+ player->change_path = NULL;
+ }
- media_player_set_folder(mp, player->path, num_of_items);
+ media_player_change_folder_complete(mp, player->path, ret);
return FALSE;
}
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index d56640f..0ef0ac3 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -591,6 +591,30 @@ static void parse_folder_list(gpointer data, gpointer user_data)
dbus_message_iter_close_container(array, &entry);
}
+void media_player_change_folder_complete(struct media_player *mp,
+ const char *path, int ret)
+{
+ struct media_folder *folder = mp->scope;
+ DBusMessage *reply;
+
+ if (folder == NULL || folder->msg == NULL)
+ return;
+
+ if (ret < 0) {
+ reply = btd_error_failed(folder->msg, strerror(-ret));
+ goto done;
+ }
+
+ media_player_set_folder(mp, path, ret);
+
+ reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(folder->msg);
+ folder->msg = NULL;
+}
+
void media_player_list_complete(struct media_player *mp, GSList *items,
int err)
{
@@ -867,7 +891,43 @@ static struct media_folder *media_player_find_folder(struct media_player *mp,
static DBusMessage *media_folder_change_folder(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return btd_error_failed(msg, strerror(ENOTSUP));
+ struct media_player *mp = data;
+ struct media_folder *folder = mp->scope;
+ struct player_callback *cb = mp->cb;
+ const char *path;
+ int err;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_failed(msg, strerror(EINVAL));
+
+ if (folder->msg != NULL)
+ return btd_error_failed(msg, strerror(EBUSY));
+
+ folder = media_player_find_folder(mp, path);
+ if (folder == NULL)
+ return btd_error_failed(msg, strerror(EINVAL));
+
+ if (mp->scope == folder)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ if (folder == mp->playlist || folder == mp->folder) {
+ media_player_change_scope(mp, folder);
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ }
+
+ if (cb->cbs->change_folder == NULL)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid,
+ cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ mp->scope->msg = dbus_message_ref(msg);
+
+ return NULL;
}
static gboolean folder_name_exists(const GDBusPropertyTable *property,
@@ -932,7 +992,7 @@ static const GDBusMethodTable media_folder_methods[] = {
GDBUS_ARGS({ "filter", "a{sv}" }),
GDBUS_ARGS({ "items", "a{oa{sv}}" }),
media_folder_list_items) },
- { GDBUS_EXPERIMENTAL_METHOD("ChangeFolder",
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ChangeFolder",
GDBUS_ARGS({ "folder", "o" }), NULL,
media_folder_change_folder) },
{ }
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 4103742..151cabd 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -93,6 +93,8 @@ struct media_item *media_player_create_item(struct media_player *mp,
void media_item_set_playable(struct media_item *item, bool value);
void media_player_list_complete(struct media_player *mp, GSList *items,
int err);
+void media_player_change_folder_complete(struct media_player *player,
+ const char *path, int ret);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 10/16] audio/AVRCP: Add support for PlayItem command
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (8 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 09/16] audio/player: Add implementation of MediaFolder.ChangeFolder Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 11/16] audio/player: Add implementation of MediaItem.Play Luiz Augusto von Dentz
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for PlayItem command via player callback.
---
profiles/audio/avrcp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/player.h | 2 ++
2 files changed, 50 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index a35477e..632cd64 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -105,6 +105,7 @@
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
+#define AVRCP_PLAY_ITEM 0x74
#define AVRCP_GENERAL_REJECT 0xA0
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
@@ -2630,6 +2631,52 @@ static int ct_change_folder(struct media_player *mp, const char *path,
return 0;
}
+static void avrcp_play_item(struct avrcp *session, uint64_t uid)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 11];
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_PLAY_ITEM;
+ pdu->params_len = htons(11);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ pdu->params[0] = player->scope;
+ bt_put_be64(uid, &pdu->params[1]);
+ bt_put_be16(player->uid_counter, &pdu->params[9]);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ NULL, session);
+}
+
+static int ct_play_item(struct media_player *mp, const char *name,
+ uint64_t uid, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+
+ if (player->p != NULL)
+ return -EBUSY;
+
+ session = player->sessions->data;
+
+ if (g_strrstr(name, "/NowPlaying"))
+ player->scope = 0x03;
+ else
+ player->scope = 0x01;
+
+ avrcp_play_item(session, uid);
+
+ return 0;
+}
+
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
@@ -2641,6 +2688,7 @@ static const struct media_player_callback ct_cbs = {
.rewind = ct_rewind,
.list_items = ct_list_items,
.change_folder = ct_change_folder,
+ .play_item = ct_play_item,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 151cabd..7d4344c 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -58,6 +58,8 @@ struct media_player_callback {
uint32_t start, uint32_t end, void *user_data);
int (*change_folder) (struct media_player *mp, const char *path,
uint64_t uid, void *user_data);
+ int (*play_item) (struct media_player *mp, const char *name,
+ uint64_t uid, void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 11/16] audio/player: Add implementation of MediaItem.Play
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (9 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 10/16] audio/AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 12/16] audio/AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/player.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 0ef0ac3..d0ecc6c 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1387,7 +1387,22 @@ void media_player_set_playlist_item(struct media_player *mp, uint64_t uid)
static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- return btd_error_failed(msg, strerror(ENOTSUP));
+ struct media_item *item = data;
+ struct media_player *mp = item->player;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (!item->playable)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ if (cb->cbs->play_item == NULL)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 12/16] audio/AVRCP: Add support for AddToNowPlaying command
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (10 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 11/16] audio/player: Add implementation of MediaItem.Play Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 13/16] audio/player: Add implementation of MediaItem.AddToNowPlaying Luiz Augusto von Dentz
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for AddToNowPlaying command via player callback.
---
profiles/audio/avrcp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/player.h | 2 ++
2 files changed, 50 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 632cd64..31fcaad 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -106,6 +106,7 @@
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_PLAY_ITEM 0x74
+#define AVRCP_ADD_TO_NOW_PLAYING 0x90
#define AVRCP_GENERAL_REJECT 0xA0
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
@@ -2677,6 +2678,52 @@ static int ct_play_item(struct media_player *mp, const char *name,
return 0;
}
+static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 11];
+ struct avrcp_player *player = session->player;
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_ADD_TO_NOW_PLAYING;
+ pdu->params_len = htons(11);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ pdu->params[0] = player->scope;
+ bt_put_be64(uid, &pdu->params[1]);
+ bt_put_be16(player->uid_counter, &pdu->params[9]);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ NULL, session);
+}
+
+static int ct_add_to_nowplaying(struct media_player *mp, const char *name,
+ uint64_t uid, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+
+ if (player->p != NULL)
+ return -EBUSY;
+
+ session = player->sessions->data;
+
+ if (g_strrstr(name, "/NowPlaying"))
+ player->scope = 0x03;
+ else
+ player->scope = 0x01;
+
+ avrcp_add_to_nowplaying(session, uid);
+
+ return 0;
+}
+
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
@@ -2689,6 +2736,7 @@ static const struct media_player_callback ct_cbs = {
.list_items = ct_list_items,
.change_folder = ct_change_folder,
.play_item = ct_play_item,
+ .add_to_nowplaying = ct_add_to_nowplaying,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 7d4344c..150b38a 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -60,6 +60,8 @@ struct media_player_callback {
uint64_t uid, void *user_data);
int (*play_item) (struct media_player *mp, const char *name,
uint64_t uid, void *user_data);
+ int (*add_to_nowplaying) (struct media_player *mp, const char *name,
+ uint64_t uid, void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 13/16] audio/player: Add implementation of MediaItem.AddToNowPlaying
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (11 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 12/16] audio/AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 14/16] audio/AVRCP: Add support for Search command Luiz Augusto von Dentz
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/player.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index d0ecc6c..701d366 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1408,7 +1408,23 @@ static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return btd_error_failed(msg, strerror(ENOTSUP));
+ struct media_item *item = data;
+ struct media_player *mp = item->player;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (!item->playable)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ if (cb->cbs->play_item == NULL)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid,
+ cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static gboolean item_name_exists(const GDBusPropertyTable *property,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 14/16] audio/AVRCP: Add support for Search command
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (12 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 13/16] audio/player: Add implementation of MediaItem.AddToNowPlaying Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 15/16] audio/player: Add implementation of MediaFolder.Search Luiz Augusto von Dentz
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for Search command via player callback.
---
profiles/audio/avrcp.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/player.h | 2 ++
2 files changed, 56 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 31fcaad..4dce600 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -106,6 +106,7 @@
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_PLAY_ITEM 0x74
+#define AVRCP_SEARCH 0x80
#define AVRCP_ADD_TO_NOW_PLAYING 0x90
#define AVRCP_GENERAL_REJECT 0xA0
@@ -2632,6 +2633,58 @@ static int ct_change_folder(struct media_player *mp, const char *path,
return 0;
}
+static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp_browsing_header *pdu = (void *) operands;
+ struct avrcp *session = (void *) user_data;
+ struct avrcp_player *player = session->player;
+
+ if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
+ operand_count < 7)
+ return FALSE;
+
+ player->uid_counter = bt_get_be16(&pdu->params[1]);
+
+ return FALSE;
+}
+
+static void avrcp_search(struct avrcp *session, const char *string)
+{
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 255];
+ struct avrcp_browsing_header *pdu = (void *) buf;
+ uint16_t len;
+
+ len = strlen(string);
+ if (len > 251)
+ len = 251;
+
+ memset(buf, 0, sizeof(buf));
+ bt_put_be16(0x6A, &pdu->params[0]);
+ bt_put_be16(len, &pdu->params[2]);
+ memcpy(&pdu->params[4], string, len);
+ pdu->pdu_id = AVRCP_SEARCH;
+ pdu->param_len = htons(len + 4);
+
+ len = AVRCP_BROWSING_HEADER_LENGTH + len + 4;
+
+ avctp_send_browsing_req(session->conn, buf, len, avrcp_search_rsp,
+ session);
+}
+
+static int ct_search(struct media_player *mp, const char *string,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+
+ session = player->sessions->data;
+
+ avrcp_search(session, string);
+
+ return 0;
+}
+
static void avrcp_play_item(struct avrcp *session, uint64_t uid)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 11];
@@ -2735,6 +2788,7 @@ static const struct media_player_callback ct_cbs = {
.rewind = ct_rewind,
.list_items = ct_list_items,
.change_folder = ct_change_folder,
+ .search = ct_search,
.play_item = ct_play_item,
.add_to_nowplaying = ct_add_to_nowplaying,
};
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 150b38a..eecab1b 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -58,6 +58,8 @@ struct media_player_callback {
uint32_t start, uint32_t end, void *user_data);
int (*change_folder) (struct media_player *mp, const char *path,
uint64_t uid, void *user_data);
+ int (*search) (struct media_player *mp, const char *string,
+ void *user_data);
int (*play_item) (struct media_player *mp, const char *name,
uint64_t uid, void *user_data);
int (*add_to_nowplaying) (struct media_player *mp, const char *name,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 15/16] audio/player: Add implementation of MediaFolder.Search
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (13 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 14/16] audio/AVRCP: Add support for Search command Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 16/16] audio/player: Create item on media_player_set_playlist_item Luiz Augusto von Dentz
2013-06-17 16:46 ` [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 20 +++++++--
profiles/audio/player.c | 113 +++++++++++++++++++++++++++++++++++++++---------
profiles/audio/player.h | 1 +
3 files changed, 110 insertions(+), 24 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 4dce600..135dcc7 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2585,6 +2585,8 @@ static int ct_list_items(struct media_player *mp, const char *name,
if (g_str_has_prefix(name, "/NowPlaying"))
player->scope = 0x03;
+ else if (g_str_has_suffix(name, "/search"))
+ player->scope = 0x02;
else
player->scope = 0x01;
@@ -2639,12 +2641,24 @@ static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands,
struct avrcp_browsing_header *pdu = (void *) operands;
struct avrcp *session = (void *) user_data;
struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ int ret;
- if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
- operand_count < 7)
- return FALSE;
+ if (pdu == NULL) {
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7) {
+ ret = -EINVAL;
+ goto done;
+ }
player->uid_counter = bt_get_be16(&pdu->params[1]);
+ ret = bt_get_be32(&pdu->params[3]);
+
+done:
+ media_player_search_complete(mp, ret);
return FALSE;
}
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 701d366..a502a2d 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -87,6 +87,7 @@ struct media_player {
bool searchable; /* Player searching feature */
struct media_folder *scope; /* Player current scope */
struct media_folder *folder; /* Player current folder */
+ struct media_folder *search; /* Player search folder */
struct media_folder *playlist; /* Player current playlist */
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
@@ -654,6 +655,57 @@ done:
folder->msg = NULL;
}
+static struct media_item *
+media_player_create_subfolder(struct media_player *mp, const char *name,
+ uint64_t uid)
+{
+ struct media_folder *folder = mp->scope;
+ struct media_item *item;
+ char *path;
+
+ path = g_strdup_printf("%s/%s", folder->item->name, name);
+
+ DBG("%s", path);
+
+ item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
+ uid);
+ g_free(path);
+
+ return item;
+}
+
+void media_player_search_complete(struct media_player *mp, int ret)
+{
+ struct media_folder *folder = mp->scope;
+ struct media_folder *search = mp->search;
+ DBusMessage *reply;
+
+ if (folder == NULL || folder->msg == NULL)
+ return;
+
+ if (ret < 0) {
+ reply = btd_error_failed(folder->msg, strerror(-ret));
+ goto done;
+ }
+
+ if (search == NULL) {
+ search = g_new0(struct media_folder, 1);
+ search->item = media_player_create_subfolder(mp, "search", 0);
+ mp->search = search;
+ }
+
+ search->number_of_items = ret;
+
+ reply = g_dbus_create_reply(folder->msg,
+ DBUS_TYPE_OBJECT_PATH, &search->item->path,
+ DBUS_TYPE_INVALID);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(folder->msg);
+ folder->msg = NULL;
+}
+
static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL, media_player_play) },
{ GDBUS_EXPERIMENTAL_METHOD("Pause", NULL, NULL, media_player_pause) },
@@ -707,7 +759,36 @@ static const GDBusPropertyTable media_player_properties[] = {
static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- return btd_error_failed(msg, strerror(ENOTSUP));
+ struct media_player *mp = data;
+ struct media_folder *folder = mp->scope;
+ struct player_callback *cb = mp->cb;
+ DBusMessageIter iter;
+ const char *string;
+ int err;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_failed(msg, strerror(EINVAL));
+
+ dbus_message_iter_get_basic(&iter, &string);
+
+ if (!mp->searchable || folder == mp->playlist)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ if (folder->msg != NULL)
+ return btd_error_failed(msg, strerror(EINVAL));
+
+ if (cb->cbs->search == NULL)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ err = cb->cbs->search(mp, string, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ folder->msg = dbus_message_ref(msg);
+
+ return NULL;
}
static int parse_filters(struct media_player *player, DBusMessageIter *iter,
@@ -852,6 +933,12 @@ cleanup:
g_slist_free_full(mp->scope->items, media_item_destroy);
mp->scope->items = NULL;
+ /* Destroy search folder if it exists */
+ if (mp->search != NULL) {
+ media_folder_destroy(mp->search);
+ mp->search = NULL;
+ }
+
done:
mp->scope = folder;
@@ -984,7 +1071,7 @@ static gboolean get_items(const GDBusPropertyTable *property,
}
static const GDBusMethodTable media_folder_methods[] = {
- { GDBUS_EXPERIMENTAL_METHOD("Search",
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Search",
GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
GDBUS_ARGS({ "folder", "o" }),
media_folder_search) },
@@ -1047,6 +1134,9 @@ void media_player_destroy(struct media_player *mp)
mp->path,
MEDIA_FOLDER_INTERFACE);
+ if (mp->search)
+ media_folder_destroy(mp->search);
+
g_slist_free_full(mp->pending, g_free);
g_slist_free_full(mp->folders, media_folder_destroy);
@@ -1660,25 +1750,6 @@ struct media_item *media_player_create_item(struct media_player *mp,
return item;
}
-static struct media_item *
-media_player_create_subfolder(struct media_player *mp, const char *name,
- uint64_t uid)
-{
- struct media_folder *folder = mp->scope;
- struct media_item *item;
- char *path;
-
- path = g_strdup_printf("%s/%s", folder->item->name, name);
-
- DBG("%s", path);
-
- item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
- uid);
- g_free(path);
-
- return item;
-}
-
static struct media_folder *
media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
{
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index eecab1b..9edc51b 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -101,6 +101,7 @@ void media_player_list_complete(struct media_player *mp, GSList *items,
int err);
void media_player_change_folder_complete(struct media_player *player,
const char *path, int ret);
+void media_player_search_complete(struct media_player *mp, int ret);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH BlueZ 16/16] audio/player: Create item on media_player_set_playlist_item
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (14 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 15/16] audio/player: Add implementation of MediaFolder.Search Luiz Augusto von Dentz
@ 2013-06-14 12:41 ` Luiz Augusto von Dentz
2013-06-17 16:46 ` [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-14 12:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This makes sure that even if /NowPlaying folder has not been listed the
current item is available and its metadata is updated.
---
profiles/audio/avrcp.c | 6 +--
profiles/audio/player.c | 111 +++++++++++++++++++++++++++++++++---------------
profiles/audio/player.h | 8 ++--
3 files changed, 85 insertions(+), 40 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 135dcc7..92d4527 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1965,10 +1965,10 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player,
uint8_t *operands, uint8_t count)
{
struct media_player *mp = player->user_data;
+ struct media_item *item;
int i;
- if (player->uid > 0)
- media_player_set_playlist_item(mp, player->uid);
+ item = media_player_set_playlist_item(mp, player->uid);
for (i = 0; count > 0; count--) {
uint32_t id;
@@ -1987,7 +1987,7 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player,
const char *key = metadata_to_str(id);
if (key != NULL)
- media_player_set_metadata(mp,
+ media_player_set_metadata(mp, item,
metadata_to_str(id),
&operands[i], len);
}
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index a502a2d..d29b10a 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -101,7 +101,7 @@ struct media_player {
GSList *folders;
};
-static void append_metadata(void *key, void *value, void *user_data)
+static void append_track(void *key, void *value, void *user_data)
{
DBusMessageIter *dict = user_data;
const char *strkey = key;
@@ -297,7 +297,7 @@ static gboolean get_track(const GDBusPropertyTable *property,
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict);
- g_hash_table_foreach(mp->track, append_metadata, &dict);
+ g_hash_table_foreach(mp->track, append_track, &dict);
dbus_message_iter_close_container(iter, &dict);
@@ -1289,6 +1289,7 @@ void media_player_set_status(struct media_player *mp, const char *status)
static gboolean process_metadata_changed(void *user_data)
{
struct media_player *mp = user_data;
+ const char *item;
mp->process_id = 0;
@@ -1296,11 +1297,20 @@ static gboolean process_metadata_changed(void *user_data)
mp->path, MEDIA_PLAYER_INTERFACE,
"Track");
+ item = g_hash_table_lookup(mp->track, "Item");
+ if (item == NULL)
+ return FALSE;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ item, MEDIA_ITEM_INTERFACE,
+ "Metadata");
+
return FALSE;
}
-void media_player_set_metadata(struct media_player *mp, const char *key,
- void *data, size_t len)
+void media_player_set_metadata(struct media_player *mp,
+ struct media_item *item, const char *key,
+ void *data, size_t len)
{
char *value, *curval;
@@ -1446,34 +1456,6 @@ static struct media_item *media_folder_find_item(struct media_folder *folder,
return NULL;
}
-void media_player_set_playlist_item(struct media_player *mp, uint64_t uid)
-{
- struct media_folder *folder = mp->playlist;
- struct media_item *item;
-
- DBG("%" PRIu64 "", uid);
-
- if (folder == NULL)
- return;
-
- item = media_folder_find_item(folder, uid);
- if (item == NULL) {
- warn("Item not found");
- return;
- }
-
- if (item == g_hash_table_lookup(mp->track, "Item"))
- return;
-
- if (mp->process_id == 0) {
- g_hash_table_remove_all(mp->track);
- mp->process_id = g_idle_add(process_metadata_changed, mp);
- }
-
- g_hash_table_replace(mp->track, g_strdup("Item"),
- g_strdup(item->path));
-}
-
static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -1638,6 +1620,24 @@ static gboolean metadata_exists(const GDBusPropertyTable *property, void *data)
return item->metadata != NULL;
}
+static void append_metadata(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+ const char *strkey = key;
+
+ if (strcasecmp(strkey, "Item") == 0)
+ return;
+
+ if (strcasecmp(strkey, "Duration") == 0 ||
+ strcasecmp(strkey, "TrackNumber") == 0 ||
+ strcasecmp(strkey, "NumberOfTracks") == 0) {
+ uint32_t num = atoi(value);
+ dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+ } else {
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+ }
+}
+
static gboolean get_metadata(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -1695,12 +1695,12 @@ void media_item_set_playable(struct media_item *item, bool value)
MEDIA_ITEM_INTERFACE, "Playable");
}
-struct media_item *media_player_create_item(struct media_player *mp,
+static struct media_item *media_folder_create_item(struct media_player *mp,
+ struct media_folder *folder,
const char *name,
player_item_type_t type,
uint64_t uid)
{
- struct media_folder *folder = mp->scope;
struct media_item *item;
const char *strtype;
@@ -1750,6 +1750,14 @@ struct media_item *media_player_create_item(struct media_player *mp,
return item;
}
+struct media_item *media_player_create_item(struct media_player *mp,
+ const char *name,
+ player_item_type_t type,
+ uint64_t uid)
+{
+ return media_folder_create_item(mp, mp->scope, name, type, uid);
+}
+
static struct media_folder *
media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
{
@@ -1828,3 +1836,38 @@ void media_player_set_callbacks(struct media_player *mp,
mp->cb = cb;
}
+
+struct media_item *media_player_set_playlist_item(struct media_player *mp,
+ uint64_t uid)
+{
+ struct media_folder *folder = mp->playlist;
+ struct media_item *item;
+
+ DBG("%" PRIu64 "", uid);
+
+ if (folder == NULL)
+ return NULL;
+
+ item = media_folder_create_item(mp, folder, NULL,
+ PLAYER_ITEM_TYPE_AUDIO, uid);
+ if (item == NULL)
+ return NULL;
+
+ if (mp->track != item->metadata) {
+ g_hash_table_unref(mp->track);
+ mp->track = g_hash_table_ref(item->metadata);
+ }
+
+ if (item == g_hash_table_lookup(mp->track, "Item"))
+ return item;
+
+ if (mp->process_id == 0) {
+ g_hash_table_remove_all(mp->track);
+ mp->process_id = g_idle_add(process_metadata_changed, mp);
+ }
+
+ g_hash_table_replace(mp->track, g_strdup("Item"),
+ g_strdup(item->path));
+
+ return item;
+}
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 9edc51b..ac2a3da 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -75,8 +75,9 @@ void media_player_set_setting(struct media_player *mp, const char *key,
const char *value);
const char *media_player_get_status(struct media_player *mp);
void media_player_set_status(struct media_player *mp, const char *status);
-void media_player_set_metadata(struct media_player *mp, const char *key,
- void *data, size_t len);
+void media_player_set_metadata(struct media_player *mp,
+ struct media_item *item, const char *key,
+ void *data, size_t len);
void media_player_set_type(struct media_player *mp, const char *type);
void media_player_set_subtype(struct media_player *mp, const char *subtype);
void media_player_set_name(struct media_player *mp, const char *name);
@@ -85,7 +86,8 @@ void media_player_set_searchable(struct media_player *mp, bool enabled);
void media_player_set_folder(struct media_player *mp, const char *path,
uint32_t items);
void media_player_set_playlist(struct media_player *mp, const char *name);
-void media_player_set_playlist_item(struct media_player *mp, uint64_t uid);
+struct media_item *media_player_set_playlist_item(struct media_player *mp,
+ uint64_t uid);
struct media_item *media_player_create_folder(struct media_player *mp,
const char *name,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (15 preceding siblings ...)
2013-06-14 12:41 ` [PATCH BlueZ 16/16] audio/player: Create item on media_player_set_playlist_item Luiz Augusto von Dentz
@ 2013-06-17 16:46 ` Luiz Augusto von Dentz
16 siblings, 0 replies; 18+ messages in thread
From: Luiz Augusto von Dentz @ 2013-06-17 16:46 UTC (permalink / raw)
To: linux-bluetooth@vger.kernel.org
Hi,
On Fri, Jun 14, 2013 at 3:41 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> v2: Fixes ChangeFolder not working for subfolders past /Filesystem
> v3: Add Search implementation, fix not forwarding errors properly and add
> patch to create current item if it doesn't exist.
>
> This set of patches implements most of the functionality of MediaFolder and
> MediaItem interfaces.
>
> Some remarks:
>
> - ChangeFolder can only change one level at time, this perhaps gonna be
> changed in the future but for now this matches the limitation of AVRCP
> itself.
> - ListItems returns the items in the correct order but GetManagedObjects
> does the reverse order due to use of g_slist_prepend.
> - PlayItem and AddToNowPlaying works fine with iPhone but with WP8 is not
> working probably because the UIDCounter changes more frequently
> which needs some new notifications that are currently not implemented.
>
> This can be tested using tools/mpris-player which will browse the NowPlaying
> list and show which item is currently being played, but the gnome extention
> itself needs patching which can be found here:
>
> https://github.com/Vudentz/gnome-shell-extensions-mediaplayer
>
> Or use the command line tool: tools/bluetooth-player
>
> Luiz Augusto von Dentz (16):
> audio/player: Split item creation
> audio/AVRCP: Add browsed flag to player
> audio/AVRCP: Add support for GetFolderItems command
> audio/player: Add implementation of MediaFolder.ListItems
> audio/player: Add support for setting current Item object
> audio/player: Add function media_item_set_playable
> audio/player: Add support for MediaItem.Metadata property
> audio/AVRCP: Add support for ChangePath command
> audio/player: Add implementation of MediaFolder.ChangeFolder
> audio/AVRCP: Add support for PlayItem command
> audio/player: Add implementation of MediaItem.Play
> audio/AVRCP: Add support for AddToNowPlaying command
> audio/player: Add implementation of MediaItem.AddToNowPlaying
> audio/AVRCP: Add support for Search command
> audio/player: Add implementation of MediaFolder.Search
> audio/player: Create item on media_player_set_playlist_item
>
> profiles/audio/avrcp.c | 488 ++++++++++++++++++++++++++++-
> profiles/audio/player.c | 804 ++++++++++++++++++++++++++++++++++++++++--------
> profiles/audio/player.h | 35 ++-
> 3 files changed, 1187 insertions(+), 140 deletions(-)
>
> --
> 1.8.1.4
This patch-set has been applied.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 18+ messages in thread