* [PATCH BlueZ 01/13] player: Split item creation
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 02/13] AVRCP: Add browsed flag to player Luiz Augusto von Dentz
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 | 354 +++++++++++++++++++++++++++++++++++-------------
profiles/audio/player.h | 11 +-
3 files changed, 269 insertions(+), 100 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 4558407..39882e4 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2138,7 +2138,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)
@@ -2146,7 +2146,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..3944807 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,105 @@ static DBusMessage *media_folder_list_items(DBusConnection *conn,
return btd_error_failed(msg, strerror(ENOTSUP));
}
+static struct media_folder *
+media_player_find_folder_by_path(struct media_player *mp, const char *path)
+{
+ struct media_folder *scope = mp->scope;
+ struct media_folder *folder = mp->folder;
+ struct media_folder *playlist = mp->playlist;
+ GSList *l;
+
+ DBG("%s", path);
+
+ if (scope == NULL)
+ return NULL;
+
+ if (g_str_equal(path, scope->item->path))
+ return scope;
+
+ if (playlist != NULL && g_str_equal(path, playlist->item->path))
+ return playlist;
+
+ if (scope->parent != NULL &&
+ g_str_equal(path, scope->parent->item->path))
+ return scope->parent;
+
+ if (folder == NULL)
+ return NULL;
+
+ if (g_str_equal(path, folder->item->path))
+ return folder;
+
+ for (l = folder->subfolders; l; l = l->next) {
+ folder = l->data;
+
+ if (g_str_equal(folder->item->path, path))
+ return folder;
+ }
+
+ return NULL;
+}
+
+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 DBusMessage *media_folder_change_folder(DBusConnection *conn,
DBusMessage *msg, void *data)
@@ -637,7 +741,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 +753,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 +770,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 +813,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 +849,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,92 +1109,75 @@ 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,
- uint32_t number_of_items)
-{
- struct media_folder *folder;
-
- folder = mp->folder;
-
- 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;
- }
- }
-
- 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,
+static struct media_folder *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;
+ struct media_folder *folder = l->data;
- if (item->type != PLAYER_ITEM_TYPE_FOLDER)
- continue;
-
- if (g_str_equal(item->name, name))
- return item;
+ if (g_str_has_suffix(folder->item->name, name))
+ return folder;
}
- return NULL;
+ return media_player_find_folder_by_path(mp, name);
}
void media_player_set_folder(struct media_player *mp, const char *name,
uint32_t number_of_items)
{
- struct media_item *item;
+ struct media_folder *folder;
DBG("%s number of items %u", name, number_of_items);
- 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);
+ folder->number_of_items = number_of_items;
+
+ media_player_set_scope(mp, folder);
}
void media_player_set_playlist(struct media_player *mp, const char *name)
{
- struct media_item *item;
+ struct media_folder *folder;
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;
}
- mp->playlist = item;
+ mp->playlist = folder;
g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
MEDIA_PLAYER_INTERFACE, "Playlist");
}
+static struct media_item *media_folder_find_item(struct media_folder *folder,
+ uint64_t uid)
+{
+ GSList *l;
+
+ if (uid == 0)
+ return NULL;
+
+ for (l = folder->items; l; l = l->next) {
+ struct media_item *item = l->data;
+
+ if (item->uid == uid)
+ return item;
+ }
+
+ return NULL;
+}
+
static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -1238,22 +1324,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 +1364,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] 14+ messages in thread* [PATCH BlueZ 02/13] AVRCP: Add browsed flag to player
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 01/13] player: Split item creation Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 03/13] AVRCP: Add support for GetFolderItems command Luiz Augusto von Dentz
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 39882e4..5477016 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;
@@ -2030,6 +2031,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]);
@@ -2375,7 +2377,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] 14+ messages in thread* [PATCH BlueZ 03/13] AVRCP: Add support for GetFolderItems command
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 01/13] player: Split item creation Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 02/13] AVRCP: Add browsed flag to player Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 04/13] player: Add implementation of MediaFolder.ListItems Luiz Augusto von Dentz
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 5477016..9b54ae9 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;
@@ -2013,6 +2023,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,
@@ -2270,6 +2432,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,
@@ -2279,6 +2468,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,
@@ -2495,11 +2685,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] 14+ messages in thread* [PATCH BlueZ 04/13] player: Add implementation of MediaFolder.ListItems
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (2 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 03/13] AVRCP: Add support for GetFolderItems command Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 05/13] player: Add support for setting current Item object Luiz Augusto von Dentz
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 2 +
profiles/audio/player.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++-
profiles/audio/player.h | 2 +
3 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 9b54ae9..f86dc9a 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2135,6 +2135,8 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
}
done:
+ media_player_list_complete(player->user_data, p->items);
+
g_slist_free(p->items);
g_free(p);
player->p = NULL;
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 3944807..fcaee5c 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,57 @@ 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)
+{
+ struct media_folder *folder = mp->scope;
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ if (folder == NULL || folder->msg == NULL)
+ return;
+
+ 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);
+
+ 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 +677,83 @@ 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;
+ 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_UINT32)
+ return -EINVAL;
+
+ if (strcasecmp(key, "Start") == 0)
+ dbus_message_iter_get_basic(&entry, start);
+ else if (strcasecmp(key, "End") == 0)
+ dbus_message_iter_get_basic(&entry, 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 struct media_folder *
@@ -696,6 +821,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);
}
@@ -795,7 +923,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) },
@@ -830,6 +958,9 @@ static void media_player_set_scope(struct media_player *mp,
return;
}
+ if (mp->scope->msg != NULL)
+ return;
+
return media_player_change_scope(mp, folder);
}
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index e7a885e..0f75f4f 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -87,6 +87,8 @@ 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);
+
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] 14+ messages in thread* [PATCH BlueZ 05/13] player: Add support for setting current Item object
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (3 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 04/13] player: Add implementation of MediaFolder.ListItems Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 06/13] player: Add function media_item_set_playable Luiz Augusto von Dentz
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 f86dc9a..6ae0a68 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1896,6 +1896,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 fcaee5c..91a6e0a 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,
@@ -1309,6 +1311,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 0f75f4f..843c71b 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] 14+ messages in thread* [PATCH BlueZ 06/13] player: Add function media_item_set_playable
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (4 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 05/13] player: Add support for setting current Item object Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 07/13] player: Add support for MediaItem.Metadata property Luiz Augusto von Dentz
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 6ae0a68..c141d24 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2031,6 +2031,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;
@@ -2049,7 +2050,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 91a6e0a..d345d38 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1485,6 +1485,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 843c71b..4e15c36 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);
void media_player_set_callbacks(struct media_player *mp,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ 07/13] player: Add support for MediaItem.Metadata property
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (5 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 06/13] player: Add function media_item_set_playable Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 08/13] AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 d345d38..103a19b 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 {
@@ -799,6 +800,9 @@ media_player_find_folder_by_path(struct media_player *mp, const char *path)
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);
@@ -1465,6 +1469,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) },
@@ -1482,6 +1517,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 },
{ }
};
@@ -1540,8 +1577,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] 14+ messages in thread* [PATCH BlueZ 08/13] AVRCP: Add support for ChangePath command
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (6 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 07/13] player: Add support for MediaItem.Metadata property Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:33 ` [PATCH BlueZ 09/13] player: Add implementation of MediaFolder.ChangeFolder Luiz Augusto von Dentz
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 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 c141d24..79299fe 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -101,8 +101,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 */
@@ -192,8 +193,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;
@@ -2187,6 +2190,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 = (void *)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,
@@ -2197,7 +2226,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;
@@ -2229,12 +2258,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;
}
@@ -2471,6 +2498,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(player->path, 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,
@@ -2481,6 +2543,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,
@@ -2593,6 +2656,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 4e15c36..75ce36b 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] 14+ messages in thread* [PATCH BlueZ 09/13] player: Add implementation of MediaFolder.ChangeFolder
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (7 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 08/13] AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
@ 2013-05-27 11:33 ` Luiz Augusto von Dentz
2013-05-27 11:34 ` [PATCH BlueZ 10/13] AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:33 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/player.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 103a19b..e918a0d 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -868,7 +868,42 @@ done:
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(EINVAL));
+
+ folder = media_player_find_folder_by_path(mp, path);
+ if (folder == NULL)
+ return btd_error_failed(msg, strerror(EINVAL));
+
+ if (mp->scope == folder)
+ goto done;
+
+ if (folder == mp->playlist || folder == mp->folder) {
+ media_player_change_scope(mp, folder);
+ goto done;
+ }
+
+ if (cb->cbs->change_folder == NULL)
+ return btd_error_failed(msg, strerror(ENOTSUP));
+
+ err = cb->cbs->change_folder(mp, folder->item->path, folder->item->uid,
+ cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+done:
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static gboolean folder_name_exists(const GDBusPropertyTable *property,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ 10/13] AVRCP: Add support for PlayItem command
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (8 preceding siblings ...)
2013-05-27 11:33 ` [PATCH BlueZ 09/13] player: Add implementation of MediaFolder.ChangeFolder Luiz Augusto von Dentz
@ 2013-05-27 11:34 ` Luiz Augusto von Dentz
2013-05-27 11:34 ` [PATCH BlueZ 11/13] player: Add implementation of MediaItem.Play Luiz Augusto von Dentz
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:34 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 79299fe..f794b55 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -104,6 +104,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 */
@@ -2533,6 +2534,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,
@@ -2544,6 +2591,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 75ce36b..a8732f5 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] 14+ messages in thread* [PATCH BlueZ 11/13] player: Add implementation of MediaItem.Play
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (9 preceding siblings ...)
2013-05-27 11:34 ` [PATCH BlueZ 10/13] AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
@ 2013-05-27 11:34 ` Luiz Augusto von Dentz
2013-05-27 11:34 ` [PATCH BlueZ 12/13] AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
2013-05-27 11:34 ` [PATCH BlueZ 13/13] player: Add implementation of MediaItem.AddToNowPlaying Luiz Augusto von Dentz
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:34 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 e918a0d..5939086 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1381,7 +1381,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] 14+ messages in thread* [PATCH BlueZ 12/13] AVRCP: Add support for AddToNowPlaying command
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (10 preceding siblings ...)
2013-05-27 11:34 ` [PATCH BlueZ 11/13] player: Add implementation of MediaItem.Play Luiz Augusto von Dentz
@ 2013-05-27 11:34 ` Luiz Augusto von Dentz
2013-05-27 11:34 ` [PATCH BlueZ 13/13] player: Add implementation of MediaItem.AddToNowPlaying Luiz Augusto von Dentz
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:34 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 f794b55..abcf8d5 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -105,6 +105,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 */
@@ -2580,6 +2581,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,
@@ -2592,6 +2639,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 a8732f5..259110f 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] 14+ messages in thread* [PATCH BlueZ 13/13] player: Add implementation of MediaItem.AddToNowPlaying
2013-05-27 11:33 [PATCH BlueZ 00/13] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
` (11 preceding siblings ...)
2013-05-27 11:34 ` [PATCH BlueZ 12/13] AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
@ 2013-05-27 11:34 ` Luiz Augusto von Dentz
12 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2013-05-27 11:34 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 5939086..0b7b797 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1402,7 +1402,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] 14+ messages in thread