linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation
@ 2013-06-14 12:41 Luiz Augusto von Dentz
  2013-06-14 12:41 ` [PATCH BlueZ 01/16] audio/player: Split item creation Luiz Augusto von Dentz
                   ` (16 more replies)
  0 siblings, 17 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>

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


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [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

end of thread, other threads:[~2013-06-17 16:46 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH BlueZ 03/16] audio/AVRCP: Add support for GetFolderItems command Luiz Augusto von Dentz
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 ` [PATCH BlueZ 05/16] audio/player: Add support for setting current Item object 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
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 ` [PATCH BlueZ 08/16] audio/AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
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 ` [PATCH BlueZ 10/16] audio/AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
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 ` [PATCH BlueZ 12/16] audio/AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
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 ` [PATCH BlueZ 14/16] audio/AVRCP: Add support for Search command Luiz Augusto von Dentz
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).