linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1
@ 2013-02-01 21:28 Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 2/6 v2] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This interface adds support for browsing and searching in the player's
storage using AVRCP 1.4/1.5.

Some remarks about the design:

  - Exposing UIDCounter and UIDs was considered, but the spec seems to have
    missed to define the player's id persistency. There are also the fact that
    UIDCounter alone does not guarantee persistency across sessions and do not
    provide what exact items have changed, so in the end exposing these
    details will bring almost no value.
  - Indexing or caching the whole media library is not recommended, Bluetooth
    is too slow for that and even vendors such as Apple do not recommend doing
    it, so the only items keep in cache are the current listed ones.
  - Addressed vs Browsed player is done implicitly when accessed, this was done
    to simplify the API and avoid confusions between applications and players.
---
v2: Remove Buttons property for now

 doc/media-api.txt | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 171 insertions(+)

diff --git a/doc/media-api.txt b/doc/media-api.txt
index 972716e..db1575f 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -226,6 +226,177 @@ Properties	string Equalizer [readwrite]
 
 			Device object path.
 
+		string Name [readonly]
+
+			Player name
+
+		boolean Browsable [readonly]
+
+			If present indicates the player can be browsed using
+			MediaLibrary interface.
+
+			Possible values:
+
+				True: Supported and active
+				False: Supported but inactive
+
+			Note: If supported but inactive clients can enable it
+			by using MediaLibrary interface but it might interfere
+			in the playback of other players.
+
+
+		boolean Searchable [readonly]
+
+			If present indicates the player can be searched using
+			MediaLibrary interface.
+
+			Possible values:
+
+				True: Supported and active
+				False: Supported but inactive
+
+			Note: If supported but inactive clients can enable it
+			by using MediaLibrary interface but it might interfere
+			in the playback of other players.
+
+MediaFolder1 hierarchy
+======================
+
+Service		unique name (Target role)
+		org.bluez (Controller role)
+Interface	org.bluez.MediaFolder1 [Experimental]
+Object path	freely definable (Target role)
+		[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+		(Controller role)
+
+Methods		object Search(string value, dict filter)
+
+			Return a folder object containing the search result.
+
+			To list the items found use the folder object returned
+			and pass to ChangeFolder.
+
+		array{objects, properties} ListItems(dict filter)
+
+			Return a list of items found
+
+		void ChangeFolder(object folder)
+
+			Change current folder.
+
+			Note: By changing folder the items of previous folder
+			might be destroyed and have to be listed again, the
+			exception is NowPlaying folder which should be always
+			present while the player is active.
+
+Properties	uint32 NumberOfItems:
+
+			Number of items in the folder
+
+		string Name:
+
+			Folder name:
+
+			Possible values:
+				"/Filesystem/...": Filesystem scope
+				"/NowPlaying/...": NowPlaying scope
+
+			Note: /NowPlaying folder might not be listed if player
+			is stopped, folders created by Search are virtual so
+			once another Search is perform or the folder is
+			changed using ChangeFolder it will no longer be listed.
+
+Filters		uint32 Start:
+
+			Offset of the first item.
+
+			Default value: 0
+
+		uint32 End:
+
+			Offset of the last item.
+
+			Default value: NumbeOfItems
+
+		array{string} Attributes
+
+			Item properties that should be included in the list.
+
+			Possible Values:
+
+				"title", "artist", "album", "genre",
+				"number-of-tracks", "number", "duration"
+
+			Default Value: All
+
+MediaItem1 hierarchy
+====================
+
+Service		unique name (Target role)
+		org.bluez (Controller role)
+Interface	org.bluez.MediaItem1 [Experimental]
+Object path	freely definable (Target role)
+		[variable
+		prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX/itemX
+		(Controller role)
+
+Methods		void Play()
+
+			Play item
+
+		void AddtoNowPlaying()
+
+			Add item to now playing list
+
+Properties	string Name [readonly]
+
+			Item displayable name
+
+		boolean Folder [readonly]
+
+			Indicates whether the item is a folder
+
+		string Type [readonly]
+
+			Item type
+
+			Possible values:
+
+				Folder: "Mixed", "Titles", "Albums", "Artists"
+				Other Items: "Video", "Audio"
+
+		boolean Playable [readonly]
+
+			Indicates if the item can be played
+
+		string Title:
+
+			Item title name
+
+		string Artist:
+
+			Item artist name
+
+		string Album:
+
+			Item album name
+
+		string Genre:
+
+			Item genre name
+
+		uint32 NumberOfTracks:
+
+			Item album number of tracks in total
+
+		uint32 Number:
+
+			Item album number
+
+		uint32 Duration:
+
+			Item duration in milliseconds
+
 MediaEndpoint1 hierarchy
 ========================
 
-- 
1.8.1


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

* [PATCH BlueZ 2/6 v2] AVRCP: Avoid repeating command due to changed event
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
@ 2013-02-01 21:28 ` Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 3/6 v2] AVRCP: Register to addressed player changed event if supported Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Currently we are repeating the same command twice for notification, first
for changed and then latter for interim response.

To avoid this the code now just do it for interim responses, changed
responses just trigger another registration.
---
 profiles/audio/avrcp.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 728ba91..0a173bc 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1877,6 +1877,12 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 
 	event = pdu->params[0];
 
+	if (code == AVC_CTYPE_CHANGED) {
+		session->registered_events ^= (1 << event);
+		avrcp_register_notification(session, event);
+		return FALSE;
+	}
+
 	switch (event) {
 	case AVRCP_EVENT_VOLUME_CHANGED:
 		value = pdu->params[1] & 0x7F;
@@ -1900,12 +1906,8 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 
 		break;
 	case AVRCP_EVENT_TRACK_CHANGED:
-		mp = player->user_data;
-
 		avrcp_get_element_attributes(session);
-
-		if (code == AVC_CTYPE_CHANGED)
-			avrcp_get_play_status(session);
+		avrcp_get_play_status(session);
 
 		break;
 
@@ -1932,12 +1934,6 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 		break;
 	}
 
-	if (code == AVC_CTYPE_CHANGED) {
-		session->registered_events ^= (1 << event);
-		avrcp_register_notification(session, event);
-		return FALSE;
-	}
-
 	session->registered_events |= (1 << event);
 
 	return TRUE;
-- 
1.8.1


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

* [PATCH BlueZ 3/6 v2] AVRCP: Register to addressed player changed event if supported
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 2/6 v2] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
@ 2013-02-01 21:28 ` Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 4/6 v2] AVRCP: Get player list " Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This enables registering for addressed player changed event and handle
the interim response setting the player id and uid counter to the current
player.
---
 profiles/audio/avrcp.c | 14 ++++++++++++++
 profiles/audio/avrcp.h | 15 ++++++++-------
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 0a173bc..8187ddf 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -176,6 +176,8 @@ struct pending_pdu {
 struct avrcp_player {
 	struct avrcp_server *server;
 	GSList *sessions;
+	uint16_t id;
+	uint16_t uid_counter;
 
 	struct avrcp_player_cb *cb;
 	void *user_data;
@@ -1869,6 +1871,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 	uint8_t event;
 	uint8_t value;
 	uint8_t count;
+	uint16_t id;
 	const char *curval, *strval;
 	int i;
 
@@ -1932,6 +1935,16 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 		}
 
 		break;
+
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		id = bt_get_be16(&pdu->params[1]);
+
+		if (player->id == id)
+			break;
+
+		player->id = id;
+		player->uid_counter = bt_get_be16(&pdu->params[3]);
+		break;
 	}
 
 	session->registered_events |= (1 << event);
@@ -2112,6 +2125,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
 		case AVRCP_EVENT_STATUS_CHANGED:
 		case AVRCP_EVENT_TRACK_CHANGED:
 		case AVRCP_EVENT_SETTINGS_CHANGED:
+		case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
 			avrcp_register_notification(session, event);
 			break;
 		}
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index b2c0d61..3799da1 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -69,13 +69,14 @@
 #define AVRCP_PLAY_STATUS_ERROR		0xFF
 
 /* Notification events */
-#define AVRCP_EVENT_STATUS_CHANGED	0x01
-#define AVRCP_EVENT_TRACK_CHANGED	0x02
-#define AVRCP_EVENT_TRACK_REACHED_END	0x03
-#define AVRCP_EVENT_TRACK_REACHED_START	0x04
-#define AVRCP_EVENT_SETTINGS_CHANGED	0x08
-#define AVRCP_EVENT_VOLUME_CHANGED	0x0d
-#define AVRCP_EVENT_LAST		AVRCP_EVENT_VOLUME_CHANGED
+#define AVRCP_EVENT_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED		0x02
+#define AVRCP_EVENT_TRACK_REACHED_END		0x03
+#define AVRCP_EVENT_TRACK_REACHED_START		0x04
+#define AVRCP_EVENT_SETTINGS_CHANGED		0x08
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED	0x0b
+#define AVRCP_EVENT_VOLUME_CHANGED		0x0d
+#define AVRCP_EVENT_LAST			AVRCP_EVENT_VOLUME_CHANGED
 
 struct avrcp_player_cb {
 	GList *(*list_settings) (void *user_data);
-- 
1.8.1


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

* [PATCH BlueZ 4/6 v2] AVRCP: Get player list if supported
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 2/6 v2] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 3/6 v2] AVRCP: Register to addressed player changed event if supported Luiz Augusto von Dentz
@ 2013-02-01 21:28 ` Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 5/6 v2] AVRCP: Split event handing to its own functions Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

If addressed player changed is supported get the list of players
---
 profiles/audio/avrcp.c  | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
 profiles/audio/player.c | 119 +++++++++++++++++++++++++++++++++++++++
 profiles/audio/player.h |   4 ++
 3 files changed, 270 insertions(+)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 8187ddf..793b5cf 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -99,6 +99,7 @@
 #define AVRCP_REQUEST_CONTINUING	0x40
 #define AVRCP_ABORT_CONTINUING		0x41
 #define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_GET_FOLDER_ITEMS		0x71
 #define AVRCP_GENERAL_REJECT		0xA0
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
@@ -1859,6 +1860,151 @@ static void avrcp_get_element_attributes(struct avrcp *session)
 					session);
 }
 
+static const char *type_to_string(uint8_t type)
+{
+	switch (type & 0x0F) {
+	case 0x01:
+		return "Audio";
+	case 0x02:
+		return "Video";
+	case 0x03:
+		return "Audio, Video";
+	case 0x04:
+		return "Audio Broadcasting";
+	case 0x05:
+		return "Audio, Audio Broadcasting";
+	case 0x06:
+		return "Video, Audio Broadcasting";
+	case 0x07:
+		return "Audio, Video, Audio Broadcasting";
+	case 0x08:
+		return "Video Broadcasting";
+	case 0x09:
+		return "Audio, Video Broadcasting";
+	case 0x0A:
+		return "Video, Video Broadcasting";
+	case 0x0B:
+		return "Audio, Video, Video Broadcasting";
+	case 0x0C:
+		return "Audio Broadcasting, Video Broadcasting";
+	case 0x0D:
+		return "Audio, Audio Broadcasting, Video Broadcasting";
+	case 0x0E:
+		return "Video, Audio Broadcasting, Video Broadcasting";
+	case 0x0F:
+		return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+	}
+
+	return "None";
+}
+
+static const char *subtype_to_string(uint32_t subtype)
+{
+	switch (subtype & 0x03) {
+	case 0x01:
+		return "Audio Book";
+	case 0x02:
+		return "Podcast";
+	case 0x03:
+		return "Audio Book, Podcast";
+	}
+
+	return "None";
+}
+
+static void avrcp_parse_media_player_item(struct avrcp *session,
+					uint8_t *operands, uint16_t len)
+{
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	uint16_t id;
+	uint32_t subtype;
+	const char *curval, *strval;
+	uint64_t features[2];
+	char name[255];
+
+	if (len < 28)
+		return;
+
+	id = bt_get_be16(&operands[0]);
+
+	if (player->id != id)
+		return;
+
+	media_player_set_type(mp, type_to_string(operands[2]));
+
+	subtype = bt_get_be32(&operands[3]);
+
+	media_player_set_subtype(mp, subtype_to_string(subtype));
+
+	curval = media_player_get_status(mp);
+	strval = status_to_string(operands[7]);
+
+	if (g_strcmp0(curval, strval) != 0) {
+		media_player_set_status(mp, strval);
+		avrcp_get_play_status(session);
+	}
+
+	features[0] = bt_get_be64(&operands[8]);
+	features[1] = bt_get_be64(&operands[16]);
+
+	media_player_set_features(mp, features);
+
+	if (operands[26] == 0)
+		return;
+
+	memcpy(name, &operands[27], operands[26]);
+
+	media_player_set_name(mp, name);
+}
+
+static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp *session = user_data;
+	uint16_t count;
+	int i;
+
+	if (operands[3] != AVRCP_STATUS_SUCCESS || operand_count < 5)
+		return FALSE;
+
+	count = bt_get_be16(&operands[6]);
+
+	for (i = 8; count; count--) {
+		uint8_t type;
+		uint16_t len;
+
+		type = operands[i++];
+		len = bt_get_be16(&operands[i]);
+		i += 2;
+
+		if (type != 0x01) {
+			i += len;
+			continue;
+		}
+
+		avrcp_parse_media_player_item(session, &operands[i], len);
+	}
+
+	return FALSE;
+}
+
+static void avrcp_get_media_player_list(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+	pdu->param_len = htons(10);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_get_media_player_list_rsp, session);
+}
+
 static gboolean avrcp_handle_event(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
@@ -1944,6 +2090,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 
 		player->id = id;
 		player->uid_counter = bt_get_be16(&pdu->params[3]);
+		avrcp_get_media_player_list(session);
 		break;
 	}
 
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index fb750fd..b4efa70 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -57,6 +57,10 @@ struct pending_req {
 
 struct media_player {
 	char			*device;	/* Device path */
+	char			*name;		/* Player name */
+	char			*type;		/* Player type */
+	char			*subtype;	/* Player subtype */
+	uint64_t		features[2];	/* Player features */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
 	GHashTable		*track;		/* Player current track */
@@ -272,6 +276,72 @@ static gboolean get_device(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean name_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->name != NULL;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->name == NULL)
+		return FALSE;
+
+	DBG("%s", mp->name);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name);
+
+	return TRUE;
+}
+
+static gboolean type_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->type != NULL;
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->type == NULL)
+		return FALSE;
+
+	DBG("%s", mp->type);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type);
+
+	return TRUE;
+}
+
+static gboolean subtype_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->subtype != NULL;
+}
+
+static gboolean get_subtype(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->subtype == NULL)
+		return FALSE;
+
+	DBG("%s", mp->subtype);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype);
+
+	return TRUE;
+}
+
 static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -410,6 +480,12 @@ static const GDBusSignalTable media_player_signals[] = {
 };
 
 static const GDBusPropertyTable media_player_properties[] = {
+	{ "Name", "s", get_name, NULL, name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Type", "s", get_type, NULL, type_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Subtype", "s", get_subtype, NULL, subtype_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Position", "u", get_position, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Status", "s", get_status, NULL, status_exists,
@@ -452,6 +528,9 @@ void media_player_destroy(struct media_player *mp)
 	g_free(mp->status);
 	g_free(mp->path);
 	g_free(mp->device);
+	g_free(mp->subtype);
+	g_free(mp->type);
+	g_free(mp->name);
 	g_free(mp);
 }
 
@@ -618,6 +697,46 @@ void media_player_set_metadata(struct media_player *mp, const char *key,
 	g_hash_table_replace(mp->track, g_strdup(key), value);
 }
 
+void media_player_set_type(struct media_player *mp, const char *type)
+{
+	DBG("%s", type);
+
+	mp->type = g_strdup(type);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Type");
+}
+
+void media_player_set_subtype(struct media_player *mp, const char *subtype)
+{
+	DBG("%s", subtype);
+
+	mp->subtype = g_strdup(subtype);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Subtype");
+}
+
+void media_player_set_name(struct media_player *mp, const char *name)
+{
+	DBG("%s", name);
+
+	mp->name = g_strdup(name);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Name");
+}
+
+void media_player_set_features(struct media_player *mp, uint64_t *features)
+{
+	DBG("0x%08zx %08zx", features[0], features[1]);
+
+	memcpy(features, mp->features, sizeof(mp->features));
+}
+
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
 				void *user_data)
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index fec1d06..b546ba7 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -47,6 +47,10 @@ 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_type(struct media_player *mp, const char *type);
+void media_player_set_subtype(struct media_player *mp, const char *subtype);
+void media_player_set_features(struct media_player *mp, uint64_t *features);
+void media_player_set_name(struct media_player *mp, const char *name);
 
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
-- 
1.8.1


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

* [PATCH BlueZ 5/6 v2] AVRCP: Split event handing to its own functions
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2013-02-01 21:28 ` [PATCH BlueZ 4/6 v2] AVRCP: Get player list " Luiz Augusto von Dentz
@ 2013-02-01 21:28 ` Luiz Augusto von Dentz
  2013-02-01 21:28 ` [PATCH BlueZ 6/6 v2] AVRCP: Set addressed player as browsed player Luiz Augusto von Dentz
  2013-02-01 21:45 ` [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Johan Hedberg
  5 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 profiles/audio/avrcp.c | 141 +++++++++++++++++++++++++++++--------------------
 1 file changed, 85 insertions(+), 56 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 793b5cf..d1eb8ce 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2005,21 +2005,94 @@ static void avrcp_get_media_player_list(struct avrcp *session)
 				avrcp_get_media_player_list_rsp, session);
 }
 
+static void avrcp_volume_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->player;
+	uint8_t volume;
+
+	if (player == NULL)
+		return;
+
+	volume = pdu->params[1] & 0x7F;
+
+	player->cb->set_volume(volume, session->dev, player->user_data);
+}
+
+static void avrcp_status_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	uint8_t value;
+	const char *curval, *strval;
+
+	value = pdu->params[1];
+
+	curval = media_player_get_status(mp);
+	strval = status_to_string(value);
+
+	if (g_strcmp0(curval, strval) == 0)
+		return;
+
+	media_player_set_status(mp, strval);
+	avrcp_get_play_status(session);
+}
+
+static void avrcp_track_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	avrcp_get_element_attributes(session);
+	avrcp_get_play_status(session);
+
+}
+
+static void avrcp_setting_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	uint8_t count = pdu->params[1];
+	int i;
+
+	for (i = 2; count > 0; count--, i += 2) {
+		const char *key;
+		const char *value;
+
+		key = attr_to_str(pdu->params[i]);
+		if (key == NULL)
+			continue;
+
+		value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+		if (value == NULL)
+			continue;
+
+		media_player_set_setting(mp, key, value);
+	}
+}
+
+static void avrcp_addressed_player_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->player;
+	uint16_t id = bt_get_be16(&pdu->params[1]);
+
+	if (player->id == id)
+		return;
+
+	player->id = id;
+	player->uid_counter = bt_get_le16(&pdu->params[3]);
+	avrcp_get_media_player_list(session);
+}
+
 static gboolean avrcp_handle_event(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
 	struct avrcp *session = user_data;
-	struct avrcp_player *player = session->player;
 	struct avrcp_header *pdu = (void *) operands;
-	struct media_player *mp;
 	uint8_t event;
-	uint8_t value;
-	uint8_t count;
-	uint16_t id;
-	const char *curval, *strval;
-	int i;
 
 	if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
 		return FALSE;
@@ -2034,63 +2107,19 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 
 	switch (event) {
 	case AVRCP_EVENT_VOLUME_CHANGED:
-		value = pdu->params[1] & 0x7F;
-
-		if (player)
-			player->cb->set_volume(value, session->dev,
-							player->user_data);
-
+		avrcp_volume_changed(session, pdu);
 		break;
 	case AVRCP_EVENT_STATUS_CHANGED:
-		mp = player->user_data;
-		value = pdu->params[1];
-
-		curval = media_player_get_status(mp);
-		strval = status_to_string(value);
-
-		if (g_strcmp0(curval, strval) != 0) {
-			media_player_set_status(mp, strval);
-			avrcp_get_play_status(session);
-		}
-
+		avrcp_status_changed(session, pdu);
 		break;
 	case AVRCP_EVENT_TRACK_CHANGED:
-		avrcp_get_element_attributes(session);
-		avrcp_get_play_status(session);
-
+		avrcp_track_changed(session, pdu);
 		break;
-
 	case AVRCP_EVENT_SETTINGS_CHANGED:
-		mp = player->user_data;
-		count = pdu->params[1];
-
-		for (i = 2; count > 0; count--, i += 2) {
-			const char *key;
-			const char *value;
-
-			key = attr_to_str(pdu->params[i]);
-			if (key == NULL)
-				continue;
-
-			value = attrval_to_str(pdu->params[i],
-						pdu->params[i + 1]);
-			if (value == NULL)
-				continue;
-
-			media_player_set_setting(mp, key, value);
-		}
-
+		avrcp_setting_changed(session, pdu);
 		break;
-
 	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
-		id = bt_get_be16(&pdu->params[1]);
-
-		if (player->id == id)
-			break;
-
-		player->id = id;
-		player->uid_counter = bt_get_be16(&pdu->params[3]);
-		avrcp_get_media_player_list(session);
+		avrcp_addressed_player_changed(session, pdu);
 		break;
 	}
 
-- 
1.8.1


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

* [PATCH BlueZ 6/6 v2] AVRCP: Set addressed player as browsed player
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2013-02-01 21:28 ` [PATCH BlueZ 5/6 v2] AVRCP: Split event handing to its own functions Luiz Augusto von Dentz
@ 2013-02-01 21:28 ` Luiz Augusto von Dentz
  2013-02-01 21:45 ` [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Johan Hedberg
  5 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 21:28 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This send SetBrowsedPlayer after getting the player details.
---
 profiles/audio/avrcp.c  |  73 +++++++++++++++++++++++--
 profiles/audio/player.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++
 profiles/audio/player.h |   2 +
 3 files changed, 209 insertions(+), 5 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index d1eb8ce..00eeea1 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -99,6 +99,7 @@
 #define AVRCP_REQUEST_CONTINUING	0x40
 #define AVRCP_ABORT_CONTINUING		0x41
 #define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_BROWSED_PLAYER	0x70
 #define AVRCP_GET_FOLDER_ITEMS		0x71
 #define AVRCP_GENERAL_REJECT		0xA0
 
@@ -1912,6 +1913,68 @@ static const char *subtype_to_string(uint32_t subtype)
 	return "None";
 }
 
+static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	uint32_t items;
+	char **folders, *path;
+	uint8_t depth, count;
+	int i;
+
+	if (operands[3] != AVRCP_STATUS_SUCCESS || operand_count < 13)
+		return FALSE;
+
+	player->uid_counter = bt_get_be16(&operands[4]);
+
+	items = bt_get_be32(&operands[6]);
+
+	depth = operands[13];
+
+	folders = g_new0(char *, depth + 1);
+
+	for (i = 14, count = 0; count < depth; count++) {
+		char *part;
+		uint8_t len;
+
+		len = operands[i++];
+		part = g_memdup(&operands[i], len);
+		i += len;
+		folders[count] = part;
+	}
+
+	path = g_build_pathv("/", folders);
+	g_strfreev(folders);
+
+	media_player_set_folder(mp, path, items);
+
+	g_free(path);
+
+	return FALSE;
+}
+
+static void avrcp_set_browsed_player(struct avrcp *session,
+						struct avrcp_player *player)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 2];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+	uint16_t id;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_SET_BROWSED_PLAYER;
+	id = htons(player->id);
+	memcpy(pdu->params, &id, 2);
+	pdu->param_len = htons(2);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_set_browsed_player_rsp, session);
+}
+
 static void avrcp_parse_media_player_item(struct avrcp *session,
 					uint8_t *operands, uint16_t len)
 {
@@ -1950,12 +2013,12 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
 
 	media_player_set_features(mp, features);
 
-	if (operands[26] == 0)
-		return;
-
-	memcpy(name, &operands[27], operands[26]);
+	if (operands[26] != 0) {
+		memcpy(name, &operands[27], operands[26]);
+		media_player_set_name(mp, name);
+	}
 
-	media_player_set_name(mp, name);
+	avrcp_set_browsed_player(session, player);
 }
 
 static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index b4efa70..2f4ea83 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -43,6 +43,7 @@
 #include "error.h"
 
 #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
 
 struct player_callback {
 	const struct media_player_callback *cbs;
@@ -55,12 +56,18 @@ struct pending_req {
 	const char *value;
 };
 
+struct media_folder {
+	char			*name;		/* Folder name */
+	uint32_t		items;		/* Number of items */
+};
+
 struct media_player {
 	char			*device;	/* Device path */
 	char			*name;		/* Player name */
 	char			*type;		/* Player type */
 	char			*subtype;	/* Player subtype */
 	uint64_t		features[2];	/* Player features */
+	struct media_folder	*folder;	/* Player currenct folder */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
 	GHashTable		*track;		/* Player current track */
@@ -505,6 +512,104 @@ static const GDBusPropertyTable media_player_properties[] = {
 	{ }
 };
 
+static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static DBusMessage *media_folder_list_items(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+
+static DBusMessage *media_folder_change_folder(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static gboolean folder_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	return mp->folder->name != NULL;
+}
+
+static gboolean get_folder_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->folder == NULL || mp->folder->name == NULL)
+		return FALSE;
+
+	DBG("%s", mp->folder->name);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&mp->folder->name);
+
+	return TRUE;
+}
+
+static gboolean items_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->folder != NULL;
+}
+
+static gboolean get_items(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	DBG("%u", mp->folder->items);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+							&mp->folder->items);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable media_folder_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("Search",
+			GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
+			GDBUS_ARGS({ "folder", "o" }),
+			media_folder_search) },
+	{ GDBUS_EXPERIMENTAL_METHOD("ListItems",
+			GDBUS_ARGS({ "filter", "a{sv}" }),
+			GDBUS_ARGS({ "items", "a{oa{sv}}" }),
+			media_folder_list_items) },
+	{ GDBUS_EXPERIMENTAL_METHOD("ChangeFolder",
+			GDBUS_ARGS({ "folder", "o" }), NULL,
+			media_folder_change_folder) },
+	{ }
+};
+
+static const GDBusPropertyTable media_folder_properties[] = {
+	{ "Name", "s", get_folder_name, NULL, folder_name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "NumberOfItems", "u", get_items, NULL, items_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static void media_folder_destroy(struct media_folder *folder)
+{
+	g_free(folder->name);
+	g_free(folder);
+}
+
 void media_player_destroy(struct media_player *mp)
 {
 	DBG("%s", mp->path);
@@ -521,6 +626,13 @@ void media_player_destroy(struct media_player *mp)
 	if (mp->process_id > 0)
 		g_source_remove(mp->process_id);
 
+	if (mp->folder) {
+		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_timer_destroy(mp->progress);
@@ -730,6 +842,33 @@ void media_player_set_name(struct media_player *mp, const char *name)
 					"Name");
 }
 
+void media_player_set_folder(struct media_player *mp, const char *path,
+								uint32_t items)
+{
+	struct media_folder *folder;
+
+	DBG("%s items %u", path, items);
+
+	if (mp->folder != NULL)
+		media_folder_destroy(mp->folder);
+
+	folder = g_new0(struct media_folder, 1);
+	folder->name = g_strdup(path);
+	folder->items = items;
+	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;
+	}
+}
+
 void media_player_set_features(struct media_player *mp, uint64_t *features)
 {
 	DBG("0x%08zx %08zx", features[0], features[1]);
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index b546ba7..1ac9800 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -51,6 +51,8 @@ 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_features(struct media_player *mp, uint64_t *features);
 void media_player_set_name(struct media_player *mp, const char *name);
+void media_player_set_folder(struct media_player *mp, const char *path,
+								uint32_t items);
 
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
-- 
1.8.1


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

* Re: [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1
  2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2013-02-01 21:28 ` [PATCH BlueZ 6/6 v2] AVRCP: Set addressed player as browsed player Luiz Augusto von Dentz
@ 2013-02-01 21:45 ` Johan Hedberg
  5 siblings, 0 replies; 7+ messages in thread
From: Johan Hedberg @ 2013-02-01 21:45 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

On Fri, Feb 01, 2013, Luiz Augusto von Dentz wrote:
> This interface adds support for browsing and searching in the player's
> storage using AVRCP 1.4/1.5.
> 
> Some remarks about the design:
> 
>   - Exposing UIDCounter and UIDs was considered, but the spec seems to have
>     missed to define the player's id persistency. There are also the fact that
>     UIDCounter alone does not guarantee persistency across sessions and do not
>     provide what exact items have changed, so in the end exposing these
>     details will bring almost no value.
>   - Indexing or caching the whole media library is not recommended, Bluetooth
>     is too slow for that and even vendors such as Apple do not recommend doing
>     it, so the only items keep in cache are the current listed ones.
>   - Addressed vs Browsed player is done implicitly when accessed, this was done
>     to simplify the API and avoid confusions between applications and players.
> ---
> v2: Remove Buttons property for now
> 
>  doc/media-api.txt | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 171 insertions(+)

All patches in this set have been applied. Thanks.

Johan

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

end of thread, other threads:[~2013-02-01 21:45 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-01 21:28 [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
2013-02-01 21:28 ` [PATCH BlueZ 2/6 v2] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
2013-02-01 21:28 ` [PATCH BlueZ 3/6 v2] AVRCP: Register to addressed player changed event if supported Luiz Augusto von Dentz
2013-02-01 21:28 ` [PATCH BlueZ 4/6 v2] AVRCP: Get player list " Luiz Augusto von Dentz
2013-02-01 21:28 ` [PATCH BlueZ 5/6 v2] AVRCP: Split event handing to its own functions Luiz Augusto von Dentz
2013-02-01 21:28 ` [PATCH BlueZ 6/6 v2] AVRCP: Set addressed player as browsed player Luiz Augusto von Dentz
2013-02-01 21:45 ` [PATCH BlueZ 1/6 v2] media-api: Add org.bluez.MediaFolder1 Johan Hedberg

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).