All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] Media API
@ 2010-09-13 14:15 Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 01/10] Add media API documentation Luiz Augusto von Dentz
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

Hi,

Now that DBus 1.4 has finally been released I guess it is time to integrate
the Media API that I've been working since the beginning of the year. For
those not aware of what it is about the discussion can be found here:

  http://thread.gmane.org/gmane.linux.bluez.kernel/4125

For now it is disabled by default to avoid a build dependency on DBus 1.4 and
to buy some time while Im working to getting it working reliably with
PulseAudio. To enable it just add the following to /etc/bluetooth/audio.conf
(won't work if DBus is < 1.3 but better use 1.3.1 or latter):

  Enable=<...,>Media

It can be used together with old unix socket, but I also add the possibility to
complete disable it by doing (highly suggested if the system is running PA):

  Disable=<...,>Socket

To register endpoint test/simple-endpoint can be used, note that it will not
stream anything, it basically configure a transport object which can be used by
e.g. gstreamer plugin:

  $./simple-endpoint
   dbus.Dictionary({'Codec': dbus.Byte(0), 'UUID': '0000110A-0000-1000-8000-00805F9B34FB', 'DelayReporting': True, 'Capabilities': dbus.Array([dbus.Byte(255), dbus.Byte(255), dbus.Byte(2), dbus.Byte(64)], signature=None)}, signature=None)
   SelectConfiguration (dbus.Array([dbus.Byte(255), dbus.Byte(255), dbus.Byte(2), dbus.Byte(32)], signature=dbus.Signature('y')))
   SetConfiguration (/org/bluez/16530/hci0/dev_00_0D_3C_B1_DD_56/fd0, dbus.Array([dbus.Byte(33), dbus.Byte(21), dbus.Byte(2), dbus.Byte(32)], signature=dbus.Signature('y')))

  $gst-launch-0.10 filesrc location=<file to stream> ! decodebin ! audioconvert ! sbcenc ! a2dpsink transport=/org/bluez/16530/hci0/dev_00_0D_3C_B1_DD_56/fd0

Or for mp3:

  $./simple-endpoint hci0 mp3source
   dbus.Dictionary({'Codec': dbus.Byte(1), 'UUID': '0000110A-0000-1000-8000-00805F9B34FB', 'Capabilities': dbus.Array([dbus.Byte(63), dbus.Byte(7), dbus.Byte(255), dbus.Byte(254)], signature=None)}, signature=None)
   SelectConfiguration (dbus.Array([dbus.Byte(63), dbus.Byte(7), dbus.Byte(255), dbus.Byte(254)], signature=dbus.Signature('y')))
   SetConfiguration (/org/bluez/15317/hci0/dev_00_0D_3C_B1_DD_56/fd2, dbus.Array([dbus.Byte(33), dbus.Byte(2), dbus.Byte(0), dbus.Byte(128)], signature=dbus.Signature('y')))

  $gst-launch-0.10 filesrc location=<file to stream> ! mp3parse ! a2dpsink transport=/org/bluez/15317/hci0/dev_00_0D_3C_B1_DD_56/fd2

Endpoints can be used with old unix socket so it should not break anything if
Media API got enabled but Socket doesn't got disabled, but note that PA will
probably try to use/lock if one register a sbcsource.

There are things that doesn't work though:

  - Simple-endpoint uses fixed configuration for sbc and mp3, also it should
  probably support streaming and more codecs via gstreamer to make it easier to
  test.
  - A2dp setconf callback need to be change since right now it expect a return
  from the endpoint but in case of MediaEndpoit this has to be async. For now
  it will block while waiting for SetConfiguration reply of the endpoint. Also
  to support BlueZ x BlueZ streams this has to take less than 4 sec. otherwise
  it will timeout on the requestor side, so timeout for SetConfiguration is set
  to 3 sec.
  - Properties such as Delay, NREC and InbandRingtone are not really writeable
  right now.

Luiz Augusto von Dentz (10):
  Add media API documentation
  Add rule to enabling talking to org.bluez.MediaEndpoint
  Add option to enable/disable unix ipc via audio.conf
  Add support for media transport in gstreamer plugin
  Add simple-endpoint test script
  Add initial implementation of org.bluez.Media spec
  Introduce headset_get_inband
  Update a2dp transport delay when it changes
  Remove local cache for nrec and inband
  Add proper checks for MediaTransport.SetProperty

 Makefile.am          |    6 +-
 audio/a2dp-codecs.h  |  116 ++++++++
 audio/a2dp.c         |  749 +++++++++++++++++++++++++++++++++++++++++--------
 audio/a2dp.h         |   15 +
 audio/avdtp.c        |   88 +++----
 audio/avdtp.h        |    5 +-
 audio/gsta2dpsink.c  |   33 ++-
 audio/gsta2dpsink.h  |    1 +
 audio/gstavdtpsink.c |  609 +++++++++++++++++++++++++++++++++++++++-
 audio/gstavdtpsink.h |    6 +
 audio/headset.c      |   10 +
 audio/headset.h      |    1 +
 audio/main.c         |    9 -
 audio/manager.c      |   66 +++++
 audio/manager.h      |    2 +
 audio/media.c        |  689 +++++++++++++++++++++++++++++++++++++++++++++
 audio/media.h        |   54 ++++
 audio/sink.c         |  179 ++-----------
 audio/source.c       |  174 ++----------
 audio/transport.c    |  758 ++++++++++++++++++++++++++++++++++++++++++++++++++
 audio/transport.h    |   36 +++
 audio/unix.c         |    1 +
 doc/media-api.txt    |  169 +++++++++++
 src/bluetooth.conf   |    1 +
 test/simple-endpoint |  126 +++++++++
 25 files changed, 3395 insertions(+), 508 deletions(-)
 create mode 100644 audio/a2dp-codecs.h
 create mode 100644 audio/media.c
 create mode 100644 audio/media.h
 create mode 100644 audio/transport.c
 create mode 100644 audio/transport.h
 create mode 100644 doc/media-api.txt
 create mode 100755 test/simple-endpoint


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

* [PATCH 01/10] Add media API documentation
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 02/10] Add rule to enabling talking to org.bluez.MediaEndpoint Luiz Augusto von Dentz
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

Media API is a replacement for the internal audio IPC which is no longer
necessary as DBus 1.3 and newer are capable of transfering file
descriptors.
---
 doc/media-api.txt |  169 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 169 insertions(+), 0 deletions(-)
 create mode 100644 doc/media-api.txt

diff --git a/doc/media-api.txt b/doc/media-api.txt
new file mode 100644
index 0000000..94fc62f
--- /dev/null
+++ b/doc/media-api.txt
@@ -0,0 +1,169 @@
+BlueZ D-Bus Media API description
+*********************************
+
+Media hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Media
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void RegisterEndpoint(object endpoint, dict properties)
+
+			Register a local end point to sender, the sender can
+			register as many end points as it likes.
+
+			Note: If the sender disconnects the end points are
+			automatically unregistered.
+
+			possible properties:
+
+				string UUID:
+
+					UUID of the profile which the endpoint
+					is for.
+
+				byte Codec:
+
+					Assigned mumber of codec that the
+					endpoint implements. The values should
+					match the profile specification which
+					is indicated by the UUID.
+
+				array{byte} Capabilities:
+
+					Capabilities blob, it is used as it is
+					so the size and byte order must match.
+
+		void UnregisterEndpoint(object endpoint)
+
+			Unregister sender end point.
+
+MediaEndpoint hierarchy
+=======================
+
+Service		unique name
+Interface	org.bluez.MediaEndpoint
+Object path	freely definable
+
+Methods		void SetConfiguration(object transport, array{byte} configuration)
+
+			Set configuration for the transport.
+
+		array{byte} SelectConfiguration(array{byte} capabilities)
+
+			Select preferable configuration from the supported
+			capabilities.
+
+			Returns a configuration which can be used to setup
+			a transport.
+
+			Note: There is no need to cache the selected
+			configuration since on success the configuration is
+			send back as parameter of SetConfiguration.
+
+		void ClearConfiguration()
+
+			Clear any configuration set.
+
+		void Release()
+
+			This method gets called when the service daemon
+			unregisters the endpoint. An endpoint can use it to do
+			cleanup tasks. There is no need to unregister the
+			endpoint, because when this method gets called it has
+			already been unregistered.
+
+MediaTransport hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.MediaTransport
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
+
+Methods		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		fd Acquire(string accesstype)
+
+			Acquire transport file descriptor.
+
+			possible accesstype:
+
+				"r" : Read only access
+
+				"w" : Write only access
+
+				"rw": Read and write access
+
+		void Release(string accesstype)
+
+			Releases file descriptor.
+
+		void SetProperty(string name, variant value)
+
+			Changes the value of the specified property. Only
+			properties that are listed a read-write can be changed.
+
+			On success this will emit a PropertyChanged signal.
+
+Signals		void PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	object Device [readonly]
+
+			Device object which the transport is connected to.
+
+		boolean ReadLock [readonly]
+
+			Transport read lock.
+
+		boolean WriteLock [readonly]
+
+			Transport read lock.
+
+		uint16 IMTU [readonly]
+
+			Transport input MTU.
+
+		uint16 OMTU [readonly]
+
+			Transport output MTU.
+
+		string UUID [readonly]
+
+			UUID of the profile which the transport is for.
+
+		byte Codec [readonly]
+
+			Assigned mumber of codec that the transport support.
+			The values should match the profile specification which
+			is indicated by the UUID.
+
+		array{byte} Configuration [readonly]
+
+			Configuration blob, it is used as it is so the size and
+			byte order must match.
+
+		uint16 Delay [readwrite]
+
+			Optional. Transport delay in 1/10 of milisecond, this
+			property is only writeable when the transport was
+			acquired by the sender.
+
+		boolean NREC [readwrite]
+
+			Optional. Indicates if echo cancelling and noise
+			reduction functions are active in the transport, this
+			property is only writeable when the transport was
+			acquired by the sender.
+
+		boolean InbandRingtone [readwrite]
+
+			Optional. Indicates if the transport support sending
+			ringtones, this property is only writeable when the
+			transport was acquired by the sender.
-- 
1.7.1


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

* [PATCH 02/10] Add rule to enabling talking to org.bluez.MediaEndpoint
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 01/10] Add media API documentation Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 03/10] Add option to enable/disable unix ipc via audio.conf Luiz Augusto von Dentz
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto Von Dentz <luiz.dentz-von@nokia.com>

---
 src/bluetooth.conf |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 56e7a83..354e5e0 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -12,6 +12,7 @@
     <allow send_destination="org.bluez"/>
     <allow send_interface="org.bluez.Agent"/>
     <allow send_interface="org.bluez.HandsfreeAgent"/>
+    <allow send_interface="org.bluez.MediaEndpoint"/>
   </policy>
 
   <policy at_console="true">
-- 
1.7.1


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

* [PATCH 03/10] Add option to enable/disable unix ipc via audio.conf
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 01/10] Add media API documentation Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 02/10] Add rule to enabling talking to org.bluez.MediaEndpoint Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 04/10] Add support for media transport in gstreamer plugin Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto Von Dentz <luiz.dentz-von@nokia.com>

---
 audio/main.c    |    9 ---------
 audio/manager.c |   12 ++++++++++++
 audio/manager.h |    1 +
 3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/audio/main.c b/audio/main.c
index 9d316ec..745c307 100644
--- a/audio/main.c
+++ b/audio/main.c
@@ -42,7 +42,6 @@
 #include "plugin.h"
 #include "log.h"
 #include "device.h"
-#include "unix.h"
 #include "headset.h"
 #include "manager.h"
 #include "gateway.h"
@@ -151,11 +150,6 @@ static int audio_init(void)
 
 	config = load_config_file(CONFIGDIR "/audio.conf");
 
-	if (unix_init() < 0) {
-		error("Unable to setup unix socket");
-		goto failed;
-	}
-
 	if (audio_manager_init(connection, config, &enable_sco) < 0)
 		goto failed;
 
@@ -174,7 +168,6 @@ static int audio_init(void)
 
 failed:
 	audio_manager_exit();
-	unix_exit();
 
 	if (connection) {
 		dbus_connection_unref(connection);
@@ -194,8 +187,6 @@ static void audio_exit(void)
 
 	audio_manager_exit();
 
-	unix_exit();
-
 	dbus_connection_unref(connection);
 }
 
diff --git a/audio/manager.c b/audio/manager.c
index 3db5987..87e7a2a 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -70,6 +70,7 @@
 #include "manager.h"
 #include "sdpd.h"
 #include "telephony.h"
+#include "unix.h"
 
 typedef enum {
 	HEADSET	= 1 << 0,
@@ -113,6 +114,7 @@ static struct enabled_interfaces enabled = {
 	.sink		= TRUE,
 	.source		= FALSE,
 	.control	= TRUE,
+	.socket		= TRUE,
 };
 
 static struct audio_adapter *find_adapter(GSList *list,
@@ -1074,6 +1076,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 			enabled.source = TRUE;
 		else if (g_str_equal(list[i], "Control"))
 			enabled.control = TRUE;
+		else if (g_str_equal(list[i], "Socket"))
+			enabled.socket = TRUE;
 	}
 	g_strfreev(list);
 
@@ -1090,6 +1094,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 			enabled.source = FALSE;
 		else if (g_str_equal(list[i], "Control"))
 			enabled.control = FALSE;
+		else if (g_str_equal(list[i], "Socket"))
+			enabled.socket = FALSE;
 	}
 	g_strfreev(list);
 
@@ -1117,6 +1123,9 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 		max_connected_headsets = i;
 
 proceed:
+	if (enabled.socket)
+		unix_init();
+
 	if (enabled.headset) {
 		telephony_init();
 		btd_register_adapter_driver(&headset_server_driver);
@@ -1152,6 +1161,9 @@ void audio_manager_exit(void)
 		config = NULL;
 	}
 
+	if (enabled.socket)
+		unix_exit();
+
 	if (enabled.headset) {
 		btd_unregister_adapter_driver(&headset_server_driver);
 		telephony_exit();
diff --git a/audio/manager.h b/audio/manager.h
index 90fe6f0..c79b761 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -29,6 +29,7 @@ struct enabled_interfaces {
 	gboolean sink;
 	gboolean source;
 	gboolean control;
+	gboolean socket;
 };
 
 int audio_manager_init(DBusConnection *conn, GKeyFile *config,
-- 
1.7.1


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

* [PATCH 04/10] Add support for media transport in gstreamer plugin
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 03/10] Add option to enable/disable unix ipc via audio.conf Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 05/10] Add simple-endpoint test script Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 Makefile.am          |    4 +-
 audio/a2dp-codecs.h  |  116 ++++++++++
 audio/gsta2dpsink.c  |   33 +++-
 audio/gsta2dpsink.h  |    1 +
 audio/gstavdtpsink.c |  609 +++++++++++++++++++++++++++++++++++++++++++++++++-
 audio/gstavdtpsink.h |    6 +
 6 files changed, 757 insertions(+), 12 deletions(-)
 create mode 100644 audio/a2dp-codecs.h

diff --git a/Makefile.am b/Makefile.am
index a8829d9..46f5449 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -307,9 +307,9 @@ audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
 				audio/rtp.h audio/ipc.h audio/ipc.c
 audio_libgstbluetooth_la_LDFLAGS = -module -avoid-version
 audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth.la \
-				@GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
+				@DBUS_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
 audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
-						$(AM_CFLAGS) @GSTREAMER_CFLAGS@
+						$(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
 endif
 endif
 
diff --git a/audio/a2dp-codecs.h b/audio/a2dp-codecs.h
new file mode 100644
index 0000000..e44634e
--- /dev/null
+++ b/audio/a2dp-codecs.h
@@ -0,0 +1,116 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define A2DP_CODEC_SBC			0x00
+#define A2DP_CODEC_MPEG12		0x01
+#define A2DP_CODEC_MPEG24		0x02
+#define A2DP_CODEC_ATRAC		0x03
+
+#define SBC_SAMPLING_FREQ_16000		(1 << 3)
+#define SBC_SAMPLING_FREQ_32000		(1 << 2)
+#define SBC_SAMPLING_FREQ_44100		(1 << 1)
+#define SBC_SAMPLING_FREQ_48000		1
+
+#define SBC_CHANNEL_MODE_MONO		(1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
+#define SBC_CHANNEL_MODE_STEREO		(1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO	1
+
+#define SBC_BLOCK_LENGTH_4		(1 << 3)
+#define SBC_BLOCK_LENGTH_8		(1 << 2)
+#define SBC_BLOCK_LENGTH_12		(1 << 1)
+#define SBC_BLOCK_LENGTH_16		1
+
+#define SBC_SUBBANDS_4			(1 << 1)
+#define SBC_SUBBANDS_8			1
+
+#define SBC_ALLOCATION_SNR		(1 << 1)
+#define SBC_ALLOCATION_LOUDNESS		1
+
+#define MPEG_CHANNEL_MODE_MONO		(1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO	(1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO	1
+
+#define MPEG_LAYER_MP1			(1 << 2)
+#define MPEG_LAYER_MP2			(1 << 1)
+#define MPEG_LAYER_MP3			1
+
+#define MPEG_SAMPLING_FREQ_16000	(1 << 5)
+#define MPEG_SAMPLING_FREQ_22050	(1 << 4)
+#define MPEG_SAMPLING_FREQ_24000	(1 << 3)
+#define MPEG_SAMPLING_FREQ_32000	(1 << 2)
+#define MPEG_SAMPLING_FREQ_44100	(1 << 1)
+#define MPEG_SAMPLING_FREQ_48000	1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct {
+	uint8_t channel_mode:4;
+	uint8_t frequency:4;
+	uint8_t allocation_method:2;
+	uint8_t subbands:2;
+	uint8_t block_length:4;
+	uint8_t min_bitpool;
+	uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+	uint8_t channel_mode:4;
+	uint8_t crc:1;
+	uint8_t layer:3;
+	uint8_t frequency:6;
+	uint8_t mpf:1;
+	uint8_t rfa:1;
+	uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+	uint8_t frequency:4;
+	uint8_t channel_mode:4;
+	uint8_t block_length:4;
+	uint8_t subbands:2;
+	uint8_t allocation_method:2;
+	uint8_t min_bitpool;
+	uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+	uint8_t layer:3;
+	uint8_t crc:1;
+	uint8_t channel_mode:4;
+	uint8_t rfa:1;
+	uint8_t mpf:1;
+	uint8_t frequency:6;
+	uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index 492fc63..ac63424 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -44,7 +44,8 @@ GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
 enum {
 	PROP_0,
 	PROP_DEVICE,
-	PROP_AUTOCONNECT
+	PROP_AUTOCONNECT,
+	PROP_TRANSPORT
 };
 
 GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
@@ -175,6 +176,16 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
 		self->device = g_value_dup_string(value);
 		break;
 
+	case PROP_TRANSPORT:
+		if (self->sink != NULL)
+			gst_avdtp_sink_set_transport(self->sink,
+				g_value_get_string(value));
+
+		if (self->transport != NULL)
+			g_free(self->transport);
+		self->transport = g_value_dup_string(value);
+		break;
+
 	case PROP_AUTOCONNECT:
 		self->autoconnect = g_value_get_boolean(value);
 
@@ -193,7 +204,7 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
 					GValue *value, GParamSpec *pspec)
 {
 	GstA2dpSink *self = GST_A2DP_SINK(object);
-	gchar *device;
+	gchar *device, *transport;
 
 	switch (prop_id) {
 	case PROP_DEVICE:
@@ -210,6 +221,13 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
 
 		g_value_set_boolean(value, self->autoconnect);
 		break;
+	case PROP_TRANSPORT:
+		if (self->sink != NULL) {
+			transport = gst_avdtp_sink_get_transport(self->sink);
+			if (transport != NULL)
+				g_value_take_string(value, transport);
+		}
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -285,6 +303,10 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 			gst_avdtp_sink_set_device(self->sink,
 					self->device);
 
+		if (self->transport != NULL)
+			gst_avdtp_sink_set_transport(self->sink,
+					self->transport);
+
 		g_object_set(G_OBJECT(self->sink), "auto-connect",
 					self->autoconnect, NULL);
 
@@ -365,6 +387,11 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
 			"Automatically attempt to connect to device",
 			DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
 
+	g_object_class_install_property(object_class, PROP_TRANSPORT,
+			g_param_spec_string("transport", "Transport",
+			"Use configured transport",
+			NULL, G_PARAM_READWRITE));
+
 	GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
 				"A2DP sink element");
 }
@@ -437,6 +464,7 @@ static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
 	self->sink = GST_AVDTP_SINK(sink);
 	self->sink_is_in_bin = TRUE;
 	g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
+	g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
 
 	gst_element_set_state(sink, GST_STATE_PAUSED);
 
@@ -674,6 +702,7 @@ static void gst_a2dp_sink_init(GstA2dpSink *self,
 	self->fakesink = NULL;
 	self->rtp = NULL;
 	self->device = NULL;
+	self->transport = NULL;
 	self->autoconnect = DEFAULT_AUTOCONNECT;
 	self->capsfilter = NULL;
 	self->newseg_event = NULL;
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index 3ff4e45..3c4510e 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -53,6 +53,7 @@ struct _GstA2dpSink {
 	GstElement *fakesink;
 
 	gchar *device;
+	gchar *transport;
 	gboolean autoconnect;
 	gboolean sink_is_in_bin;
 
diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c
index b8c8832..95c4811 100644
--- a/audio/gstavdtpsink.c
+++ b/audio/gstavdtpsink.c
@@ -37,8 +37,11 @@
 
 #include <gst/rtp/gstrtpbuffer.h>
 
+#include <dbus/dbus.h>
+
 #include "ipc.h"
 #include "rtp.h"
+#include "a2dp-codecs.h"
 
 #include "gstpragma.h"
 #include "gstavdtpsink.h"
@@ -61,11 +64,20 @@ GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug);
 		g_mutex_unlock(s->sink_lock);		\
 	} G_STMT_END
 
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
 
 struct bluetooth_data {
 	struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */
 	guint link_mtu;
 
+	DBusConnection *conn;
+	guint8 codec; /* Bluetooth transport configuration */
+	gchar *uuid;
+	guint8 *config;
+	gint config_size;
+
 	gchar buffer[BUFFER_SIZE];	/* Codec transfer buffer */
 };
 
@@ -75,7 +87,8 @@ struct bluetooth_data {
 enum {
 	PROP_0,
 	PROP_DEVICE,
-	PROP_AUTOCONNECT
+	PROP_AUTOCONNECT,
+	PROP_TRANSPORT
 };
 
 GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink,
@@ -127,6 +140,23 @@ static void gst_avdtp_sink_base_init(gpointer g_class)
 	gst_element_class_set_details(element_class, &avdtp_sink_details);
 }
 
+static void gst_avdtp_sink_transport_release(GstAvdtpSink *self)
+{
+	DBusMessage *msg;
+	const char *access_type = "w";
+
+	msg = dbus_message_new_method_call("org.bluez", self->transport,
+						"org.bluez.MediaTransport",
+						"Release");
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+					DBUS_TYPE_INVALID);
+
+	dbus_connection_send(self->data->conn, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
 static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
 {
 	GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
@@ -151,6 +181,10 @@ static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
 	}
 
 	if (self->data) {
+		if (self->transport)
+			gst_avdtp_sink_transport_release(self);
+		if (self->data->conn)
+			dbus_connection_unref(self->data->conn);
 		g_free(self->data);
 		self->data = NULL;
 	}
@@ -178,6 +212,9 @@ static void gst_avdtp_sink_finalize(GObject *object)
 	if (self->device)
 		g_free(self->device);
 
+	if (self->transport)
+		g_free(self->transport);
+
 	g_mutex_free(self->sink_lock);
 
 	G_OBJECT_CLASS(parent_class)->finalize(object);
@@ -198,6 +235,13 @@ static void gst_avdtp_sink_set_property(GObject *object, guint prop_id,
 	case PROP_AUTOCONNECT:
 		sink->autoconnect = g_value_get_boolean(value);
 		break;
+
+	case PROP_TRANSPORT:
+		if (sink->transport)
+			g_free(sink->transport);
+		sink->transport = g_value_dup_string(value);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -217,6 +261,11 @@ static void gst_avdtp_sink_get_property(GObject *object, guint prop_id,
 	case PROP_AUTOCONNECT:
 		g_value_set_boolean(value, sink->autoconnect);
 		break;
+
+	case PROP_TRANSPORT:
+		g_value_set_string(value, sink->transport);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -370,6 +419,10 @@ static gboolean gst_avdtp_sink_conf_recv_stream_fd(
 	GIOFlags flags;
 	gsize read;
 
+	/* Proceed if stream was already acquired */
+	if (self->stream != NULL)
+		goto proceed;
+
 	ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self);
 	if (ret < 0)
 		return FALSE;
@@ -380,6 +433,7 @@ static gboolean gst_avdtp_sink_conf_recv_stream_fd(
 		return FALSE;
 	}
 
+proceed:
 	/* set stream socket to nonblock */
 	GST_LOG_OBJECT(self, "setting stream socket to nonblock");
 	flags = g_io_channel_get_flags(self->stream);
@@ -733,6 +787,325 @@ static GstStructure *gst_avdtp_sink_parse_mpeg_caps(
 	return structure;
 }
 
+static GstStructure *gst_avdtp_sink_parse_sbc_raw(GstAvdtpSink *self)
+{
+	a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config;
+	GstStructure *structure;
+	GValue *value;
+	GValue *list;
+	gboolean mono, stereo;
+
+	structure = gst_structure_empty_new("audio/x-sbc");
+	value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+	/* mode */
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+		g_value_set_static_string(value, "mono");
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+		g_value_set_static_string(value, "stereo");
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+		g_value_set_static_string(value, "dual");
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+		g_value_set_static_string(value, "joint");
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "mode", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* subbands */
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	value = g_value_init(value, G_TYPE_INT);
+	if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+		g_value_set_int(value, 4);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+		g_value_set_int(value, 8);
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "subbands", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* blocks */
+	value = g_value_init(value, G_TYPE_INT);
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+		g_value_set_int(value, 16);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+		g_value_set_int(value, 12);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+		g_value_set_int(value, 8);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+		g_value_set_int(value, 4);
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "blocks", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* allocation */
+	g_value_init(value, G_TYPE_STRING);
+	list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+	if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+		g_value_set_static_string(value, "loudness");
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+		g_value_set_static_string(value, "snr");
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "allocation", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* rate */
+	g_value_init(value, G_TYPE_INT);
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+		g_value_set_int(value, 48000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+		g_value_set_int(value, 44100);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+		g_value_set_int(value, 32000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+		g_value_set_int(value, 16000);
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "rate", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* bitpool */
+	value = g_value_init(value, GST_TYPE_INT_RANGE);
+	gst_value_set_int_range(value,
+			MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+			MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+	gst_structure_set_value(structure, "bitpool", value);
+	g_value_unset(value);
+
+	/* channels */
+	mono = FALSE;
+	stereo = FALSE;
+	if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+		mono = TRUE;
+	if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+			(sbc->channel_mode &
+			BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+			(sbc->channel_mode &
+			BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+		stereo = TRUE;
+
+	if (mono && stereo) {
+		g_value_init(value, GST_TYPE_INT_RANGE);
+		gst_value_set_int_range(value, 1, 2);
+	} else {
+		g_value_init(value, G_TYPE_INT);
+		if (mono)
+			g_value_set_int(value, 1);
+		else if (stereo)
+			g_value_set_int(value, 2);
+		else {
+			GST_ERROR_OBJECT(self,
+				"Unexpected number of channels");
+			g_value_set_int(value, 0);
+		}
+	}
+
+	gst_structure_set_value(structure, "channels", value);
+	g_free(value);
+
+	return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_raw(GstAvdtpSink *self)
+{
+	a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config;
+	GstStructure *structure;
+	GValue *value;
+	GValue *list;
+	gboolean valid_layer = FALSE;
+	gboolean mono, stereo;
+
+	GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+	structure = gst_structure_empty_new("audio/mpeg");
+	value = g_new0(GValue, 1);
+	g_value_init(value, G_TYPE_INT);
+
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	g_value_set_int(value, 1);
+	gst_value_list_prepend_value(list, value);
+	g_value_set_int(value, 2);
+	gst_value_list_prepend_value(list, value);
+	gst_structure_set_value(structure, "mpegversion", list);
+	g_free(list);
+
+	/* layer */
+	GST_LOG_OBJECT(self, "setting mpeg layer");
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	if (mpeg->layer & BT_MPEG_LAYER_1) {
+		g_value_set_int(value, 1);
+		gst_value_list_prepend_value(list, value);
+		valid_layer = TRUE;
+	}
+	if (mpeg->layer & BT_MPEG_LAYER_2) {
+		g_value_set_int(value, 2);
+		gst_value_list_prepend_value(list, value);
+		valid_layer = TRUE;
+	}
+	if (mpeg->layer & BT_MPEG_LAYER_3) {
+		g_value_set_int(value, 3);
+		gst_value_list_prepend_value(list, value);
+		valid_layer = TRUE;
+	}
+	if (list) {
+		gst_structure_set_value(structure, "layer", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	if (!valid_layer) {
+		gst_structure_free(structure);
+		g_free(value);
+		return NULL;
+	}
+
+	/* rate */
+	GST_LOG_OBJECT(self, "setting mpeg rate");
+	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+		g_value_set_int(value, 48000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+		g_value_set_int(value, 44100);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+		g_value_set_int(value, 32000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+		g_value_set_int(value, 24000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+		g_value_set_int(value, 22050);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+		g_value_set_int(value, 16000);
+		gst_value_list_prepend_value(list, value);
+	}
+	g_value_unset(value);
+	if (list) {
+		gst_structure_set_value(structure, "rate", list);
+		g_free(list);
+		list = NULL;
+	}
+
+	/* channels */
+	GST_LOG_OBJECT(self, "setting mpeg channels");
+	mono = FALSE;
+	stereo = FALSE;
+	if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+		mono = TRUE;
+	if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+			(mpeg->channel_mode &
+			BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+			(mpeg->channel_mode &
+			BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+		stereo = TRUE;
+
+	if (mono && stereo) {
+		g_value_init(value, GST_TYPE_INT_RANGE);
+		gst_value_set_int_range(value, 1, 2);
+	} else {
+		g_value_init(value, G_TYPE_INT);
+		if (mono)
+			g_value_set_int(value, 1);
+		else if (stereo)
+			g_value_set_int(value, 2);
+		else {
+			GST_ERROR_OBJECT(self,
+				"Unexpected number of channels");
+			g_value_set_int(value, 0);
+		}
+	}
+	gst_structure_set_value(structure, "channels", value);
+	g_free(value);
+
+	return structure;
+}
+
+static gboolean gst_avdtp_sink_update_config(GstAvdtpSink *self)
+{
+	GstStructure *structure;
+	gchar *tmp;
+
+	switch (self->data->codec) {
+	case A2DP_CODEC_SBC:
+		structure = gst_avdtp_sink_parse_sbc_raw(self);
+		break;
+	case A2DP_CODEC_MPEG12:
+		structure = gst_avdtp_sink_parse_mpeg_raw(self);
+		break;
+	default:
+		GST_ERROR_OBJECT(self, "Unsupported configuration");
+		return FALSE;
+	}
+
+	if (structure == NULL)
+		return FALSE;
+
+	if (self->dev_caps != NULL)
+		gst_caps_unref(self->dev_caps);
+
+	self->dev_caps = gst_caps_new_full(structure, NULL);
+
+	tmp = gst_caps_to_string(self->dev_caps);
+	GST_DEBUG_OBJECT(self, "Transport configuration: %s", tmp);
+	g_free(tmp);
+
+	return TRUE;
+}
+
 static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
 {
 	sbc_capabilities_t *sbc;
@@ -743,6 +1116,9 @@ static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
 
 	GST_LOG_OBJECT(self, "updating device caps");
 
+	if (self->data->config_size != 0 && self->data->config != NULL)
+		return gst_avdtp_sink_update_config(self);
+
 	sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
 	mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
 
@@ -870,6 +1246,192 @@ static gboolean gst_avdtp_sink_event(GstBaseSink *basesink,
 	return TRUE;
 }
 
+static gboolean gst_avdtp_sink_transport_parse_property(GstAvdtpSink *self,
+							DBusMessageIter *i)
+{
+	const char *key;
+	DBusMessageIter variant_i;
+
+	if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+		GST_ERROR_OBJECT(self, "Property name not a string.");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(i, &key);
+
+	if (!dbus_message_iter_next(i))  {
+		GST_ERROR_OBJECT(self, "Property value missing");
+		return FALSE;
+	}
+
+	if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+		GST_ERROR_OBJECT(self, "Property value not a variant.");
+		return FALSE;
+	}
+
+	dbus_message_iter_recurse(i, &variant_i);
+
+	switch (dbus_message_iter_get_arg_type(&variant_i)) {
+	case DBUS_TYPE_BYTE: {
+		uint8_t value;
+		dbus_message_iter_get_basic(&variant_i, &value);
+
+		if (g_str_equal(key, "Codec") == TRUE)
+			self->data->codec = value;
+
+		break;
+	}
+	case DBUS_TYPE_UINT16: {
+		uint16_t value;
+		dbus_message_iter_get_basic(&variant_i, &value);
+
+		if (g_str_equal(key, "OMTU") == TRUE)
+			self->data->link_mtu = value;
+
+		break;
+	}
+        case DBUS_TYPE_STRING: {
+		const char *value;
+		dbus_message_iter_get_basic(&variant_i, &value);
+
+		if (g_str_equal(key, "UUID") == TRUE) {
+			g_free(self->data->uuid);
+			self->data->uuid = g_strdup(value);
+		}
+
+		break;
+	}
+	case DBUS_TYPE_ARRAY: {
+		DBusMessageIter array_i;
+		char *value;
+		int size;
+
+		dbus_message_iter_recurse(&variant_i, &array_i);
+		dbus_message_iter_get_fixed_array(&array_i, &value, &size);
+
+		if (g_str_equal(key, "Configuration")) {
+			g_free(self->data->config);
+			self->data->config = g_new0(guint8, size);
+			self->data->config_size = size;
+			memcpy(self->data->config, value, size);
+		}
+
+		break;
+	}
+	}
+
+	return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_acquire(GstAvdtpSink *self)
+{
+	DBusMessage *msg, *reply;
+	DBusError err;
+	const char *access_type = "w";
+	int fd;
+
+	dbus_error_init(&err);
+
+	if (self->data->conn == NULL)
+		self->data->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+	msg = dbus_message_new_method_call("org.bluez", self->transport,
+						"org.bluez.MediaTransport",
+						"Acquire");
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+					DBUS_TYPE_INVALID);
+
+	reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+							msg, -1, &err);
+
+	if (dbus_error_is_set(&err))
+		goto fail;
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID) == FALSE)
+		goto fail;
+
+	dbus_message_unref(reply);
+
+	self->stream = g_io_channel_unix_new(fd);
+	g_io_channel_set_close_on_unref(self->stream, TRUE);
+	GST_DEBUG_OBJECT(self, "stream_fd=%d", fd);
+
+	return TRUE;
+
+fail:
+	GST_ERROR_OBJECT(self, "Failed to acquire transport stream: %s",
+				err.message);
+
+	dbus_error_free(&err);
+
+	if (reply)
+		dbus_message_unref(msg);
+
+	return FALSE;
+}
+
+static gboolean gst_avdtp_sink_transport_get_properties(GstAvdtpSink *self)
+{
+	DBusMessage *msg, *reply;
+	DBusMessageIter arg_i, ele_i;
+	DBusError err;
+
+	dbus_error_init(&err);
+
+	/* Transport need to be acquire first to make sure the MTUs are
+	   available */
+	if (gst_avdtp_sink_transport_acquire(self) == FALSE)
+		return FALSE;
+
+	msg = dbus_message_new_method_call("org.bluez", self->transport,
+						"org.bluez.MediaTransport",
+						"GetProperties");
+	reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+							msg, -1, &err);
+
+	if (dbus_error_is_set(&err) || reply == NULL) {
+		GST_ERROR_OBJECT(self, "Failed to get transport properties: %s",
+					err.message);
+		goto fail;
+	}
+
+	if (!dbus_message_iter_init(reply, &arg_i)) {
+		GST_ERROR_OBJECT(self, "GetProperties reply has no arguments.");
+		goto fail;
+	}
+
+	if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+		GST_ERROR_OBJECT(self, "GetProperties argument is not an array.");
+		goto fail;
+	}
+
+	dbus_message_iter_recurse(&arg_i, &ele_i);
+	while (dbus_message_iter_get_arg_type(&ele_i) != DBUS_TYPE_INVALID) {
+
+		if (dbus_message_iter_get_arg_type(&ele_i) ==
+				DBUS_TYPE_DICT_ENTRY) {
+			DBusMessageIter dict_i;
+
+			dbus_message_iter_recurse(&ele_i, &dict_i);
+
+			gst_avdtp_sink_transport_parse_property(self, &dict_i);
+		}
+
+		if (!dbus_message_iter_next(&ele_i))
+			break;
+	}
+
+	return gst_avdtp_sink_update_caps(self);
+
+fail:
+	dbus_message_unref(msg);
+	dbus_message_unref(reply);
+	return FALSE;
+
+}
+
 static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
 {
 	GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
@@ -878,6 +1440,16 @@ static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
 
 	GST_INFO_OBJECT(self, "start");
 
+	self->data = g_new0(struct bluetooth_data, 1);
+
+	self->stream = NULL;
+	self->stream_caps = NULL;
+	self->mp3_using_crc = -1;
+	self->channel_mode = -1;
+
+	if (self->transport != NULL)
+		return gst_avdtp_sink_transport_get_properties(self);
+
 	self->watch_id = 0;
 
 	sk = bt_audio_service_open();
@@ -892,13 +1464,6 @@ static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
 	self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
 					G_IO_NVAL, server_callback, self);
 
-	self->data = g_new0(struct bluetooth_data, 1);
-
-	self->stream = NULL;
-	self->stream_caps = NULL;
-	self->mp3_using_crc = -1;
-	self->channel_mode = -1;
-
 	if (!gst_avdtp_sink_get_capabilities(self)) {
 		GST_ERROR_OBJECT(self, "failed to get capabilities "
 				"from device");
@@ -920,6 +1485,9 @@ static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self)
 	struct bt_new_stream_ind *ind = (void *) buf;
 	GIOError io_error;
 
+	if (self->transport != NULL)
+		return gst_avdtp_sink_conf_recv_stream_fd(self);
+
 	memset(req, 0, sizeof(buf));
 	req->h.type = BT_REQUEST;
 	req->h.name = BT_START_STREAM;
@@ -1051,6 +1619,10 @@ static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self,
 	GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
 	g_free(temp);
 
+	/* Transport already configured */
+	if (self->transport != NULL)
+		return TRUE;
+
 	structure = gst_caps_get_structure(caps, 0);
 
 	if (gst_structure_has_name(structure, "audio/x-sbc"))
@@ -1237,6 +1809,12 @@ static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass)
 					"to device", DEFAULT_AUTOCONNECT,
 					G_PARAM_READWRITE));
 
+	g_object_class_install_property(object_class, PROP_TRANSPORT,
+					g_param_spec_string("transport",
+					"Transport",
+					"Use configured transport",
+					NULL, G_PARAM_READWRITE));
+
 	GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0,
 				"A2DP headset sink element");
 }
@@ -1245,6 +1823,7 @@ static void gst_avdtp_sink_init(GstAvdtpSink *self,
 			GstAvdtpSinkClass *klass)
 {
 	self->device = NULL;
+	self->transport = NULL;
 	self->data = NULL;
 
 	self->stream = NULL;
@@ -1400,11 +1979,25 @@ void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar *dev)
 	self->device = g_strdup(dev);
 }
 
+void gst_avdtp_sink_set_transport(GstAvdtpSink *self, const gchar *trans)
+{
+	if (self->transport != NULL)
+		g_free(self->transport);
+
+	GST_LOG_OBJECT(self, "Setting transport: %s", trans);
+	self->transport = g_strdup(trans);
+}
+
 gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self)
 {
 	return g_strdup(self->device);
 }
 
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *self)
+{
+	return g_strdup(self->transport);
+}
+
 void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc)
 {
 	gint new_crc;
diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h
index eb78248..888169f 100644
--- a/audio/gstavdtpsink.h
+++ b/audio/gstavdtpsink.h
@@ -51,6 +51,7 @@ struct _GstAvdtpSink {
 	GstBaseSink sink;
 
 	gchar *device;
+	gchar *transport;
 	GIOChannel *stream;
 
 	struct bluetooth_data *data;
@@ -86,8 +87,13 @@ guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink);
 void gst_avdtp_sink_set_device(GstAvdtpSink *sink,
 		const gchar* device);
 
+void gst_avdtp_sink_set_transport(GstAvdtpSink *sink,
+		const gchar *transport);
+
 gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink);
 
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink);
+
 gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin);
 
 void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc);
-- 
1.7.1


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

* [PATCH 05/10] Add simple-endpoint test script
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 04/10] Add support for media transport in gstreamer plugin Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 06/10] Add initial implementation of org.bluez.Media spec Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 test/simple-endpoint |  126 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 126 insertions(+), 0 deletions(-)
 create mode 100755 test/simple-endpoint

diff --git a/test/simple-endpoint b/test/simple-endpoint
new file mode 100755
index 0000000..e09a528
--- /dev/null
+++ b/test/simple-endpoint
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
+A2DP_SINK_UUID = "0000110B-0000-1000-8000-00805F9B34FB"
+HFP_AG_UUID = "0000111F-0000-1000-8000-00805F9B34FB"
+HSP_AG_UUID = "00001112-0000-1000-8000-00805F9B34FB"
+
+SBC_CODEC = dbus.Byte(0x00)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+#Subbands: 4 8
+#Blocks: 4 8 12 16
+#Bitpool Range: 2-64
+SBC_CAPABILITIES = dbus.Array([dbus.Byte(0xff), dbus.Byte(0xff), dbus.Byte(2), dbus.Byte(64)])
+# JointStereo 44.1Khz Subbands: Blocks: 16 Bitpool Range: 2-32
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x15), dbus.Byte(2), dbus.Byte(32)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 32Khz 44.1Khz 48Khz
+#CRC: YES
+#Layer: 3
+#Bit Rate: All except Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0x3f), dbus.Byte(0x07), dbus.Byte(0xff), dbus.Byte(0xfe)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x02), dbus.Byte(0x00), dbus.Byte(0x80)])
+
+PCM_CODEC = dbus.Byte(0x00)
+PCM_CONFIGURATION = dbus.Array([], signature="ay")
+
+class Rejected(dbus.DBusException):
+	_dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+	exit_on_release = True
+	configuration = SBC_CONFIGURATION
+
+	def set_exit_on_release(self, exit_on_release):
+		self.exit_on_release = exit_on_release
+
+	def default_configuration(self, configuration):
+		self.configuration = configuration
+
+	@dbus.service.method("org.bluez.MediaEndpoint",
+					in_signature="", out_signature="")
+	def Release(self):
+		print "Release"
+		if self.exit_on_release:
+			mainloop.quit()
+
+	@dbus.service.method("org.bluez.MediaEndpoint",
+					in_signature="", out_signature="")
+	def ClearConfiguration(self):
+		print "ClearConfiguration"
+
+	@dbus.service.method("org.bluez.MediaEndpoint",
+					in_signature="oay", out_signature="")
+	def SetConfiguration(self, transport, config):
+		print "SetConfiguration (%s, %s)" % (transport, config)
+		return
+
+	@dbus.service.method("org.bluez.MediaEndpoint",
+					in_signature="ay", out_signature="ay")
+	def SelectConfiguration(self, caps):
+		print "SelectConfiguration (%s)" % (caps)
+		return self.configuration
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+						"org.bluez.Manager")
+
+	if len(sys.argv) > 1:
+		path = manager.FindAdapter(sys.argv[1])
+	else:
+		path = manager.DefaultAdapter()
+
+	media = dbus.Interface(bus.get_object("org.bluez", path),
+						"org.bluez.Media")
+
+	path = "/test/endpoint"
+	endpoint = Endpoint(bus, path)
+	mainloop = gobject.MainLoop()
+
+	properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+					"Codec" : SBC_CODEC,
+					"DelayReporting" : True,
+					"Capabilities" : SBC_CAPABILITIES })
+
+	if len(sys.argv) > 2:
+		if sys.argv[2] == "sbcsink":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+							"Codec" : SBC_CODEC,
+							"DelayReporting" : True,
+							"Capabilities" : SBC_CAPABILITIES })
+		if sys.argv[2] == "mp3source":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+							"Codec" : MP3_CODEC,
+							"Capabilities" : MP3_CAPABILITIES })
+			endpoint.default_configuration(MP3_CONFIGURATION)
+		if sys.argv[2] == "mp3sink":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+							"Codec" : MP3_CODEC,
+							"Capabilities" : MP3_CAPABILITIES })
+			endpoint.default_configuration(MP3_CONFIGURATION)
+		if sys.argv[2] == "hfpag" or sys.argv[2] == "hspag":
+			properties = dbus.Dictionary({ "UUID" : HFP_AG_UUID,
+							"Codec" : PCM_CODEC,
+							"Capabilities" :  PCM_CONFIGURATION })
+			endpoint.default_configuration(dbus.Array([]))
+
+	print properties
+
+	media.RegisterEndpoint(path, properties)
+
+	mainloop.run()
-- 
1.7.1


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

* [PATCH 06/10] Add initial implementation of org.bluez.Media spec
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 05/10] Add simple-endpoint test script Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 07/10] Introduce headset_get_inband Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 Makefile.am       |    2 +
 audio/a2dp.c      |  726 ++++++++++++++++++++++++++++++++++++++++++++---------
 audio/a2dp.h      |   15 ++
 audio/avdtp.c     |   88 +++----
 audio/avdtp.h     |    5 +-
 audio/manager.c   |   54 ++++
 audio/manager.h   |    1 +
 audio/media.c     |  683 +++++++++++++++++++++++++++++++++++++++++++++++++
 audio/media.h     |   52 ++++
 audio/sink.c      |  179 ++------------
 audio/source.c    |  174 ++-----------
 audio/transport.c |  671 +++++++++++++++++++++++++++++++++++++++++++++++++
 audio/transport.h |   34 +++
 audio/unix.c      |    1 +
 14 files changed, 2198 insertions(+), 487 deletions(-)
 create mode 100644 audio/media.c
 create mode 100644 audio/media.h
 create mode 100644 audio/transport.c
 create mode 100644 audio/transport.h

diff --git a/Makefile.am b/Makefile.am
index 46f5449..347205e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -139,6 +139,8 @@ builtin_sources += audio/main.c \
 			audio/avdtp.h audio/avdtp.c \
 			audio/ipc.h audio/ipc.c \
 			audio/unix.h audio/unix.c \
+			audio/media.h audio/media.c \
+			audio/transport.h audio/transport.c \
 			audio/telephony.h
 builtin_nodist += audio/telephony.c
 
diff --git a/audio/a2dp.c b/audio/a2dp.c
index ef0df17..e6daf3b 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -43,6 +43,7 @@
 #include "sink.h"
 #include "source.h"
 #include "unix.h"
+#include "media.h"
 #include "a2dp.h"
 #include "sdpd.h"
 
@@ -60,9 +61,11 @@
 #endif
 
 struct a2dp_sep {
+	struct a2dp_server *server;
+	struct media_endpoint *endpoint;
 	uint8_t type;
 	uint8_t codec;
-	struct avdtp_local_sep *sep;
+	struct avdtp_local_sep *lsep;
 	struct avdtp *session;
 	struct avdtp_stream *stream;
 	guint suspend_timer;
@@ -73,6 +76,7 @@ struct a2dp_sep {
 };
 
 struct a2dp_setup_cb {
+	a2dp_select_cb_t select_cb;
 	a2dp_config_cb_t config_cb;
 	a2dp_stream_cb_t resume_cb;
 	a2dp_stream_cb_t suspend_cb;
@@ -84,6 +88,7 @@ struct a2dp_setup {
 	struct audio_device *dev;
 	struct avdtp *session;
 	struct a2dp_sep *sep;
+	struct avdtp_remote_sep *rsep;
 	struct avdtp_stream *stream;
 	struct avdtp_error *err;
 	GSList *client_caps;
@@ -150,11 +155,11 @@ static struct audio_device *a2dp_get_dev(struct avdtp *session)
 static gboolean finalize_config(struct a2dp_setup *s)
 {
 	GSList *l;
+	struct avdtp_stream *stream = s->err ? NULL : s->stream;
 
 	setup_ref(s);
 	for (l = s->cb; l != NULL; l = l->next) {
 		struct a2dp_setup_cb *cb = l->data;
-		struct avdtp_stream *stream = s->err ? NULL : s->stream;
 
 		if (!cb->config_cb)
 			continue;
@@ -166,6 +171,7 @@ static gboolean finalize_config(struct a2dp_setup *s)
 	}
 
 	setup_unref(s);
+
 	return FALSE;
 }
 
@@ -184,6 +190,7 @@ static gboolean finalize_resume(struct a2dp_setup *s)
 	GSList *l;
 
 	setup_ref(s);
+
 	for (l = s->cb; l != NULL; l = l->next) {
 		struct a2dp_setup_cb *cb = l->data;
 
@@ -195,6 +202,7 @@ static gboolean finalize_resume(struct a2dp_setup *s)
 	}
 
 	setup_unref(s);
+
 	return FALSE;
 }
 
@@ -227,6 +235,25 @@ static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err)
 	return finalize_suspend(s);
 }
 
+static gboolean finalize_select(struct a2dp_setup *s, GSList *caps)
+{
+	GSList *l;
+
+	setup_ref(s);
+	for (l = s->cb; l != NULL; l = l->next) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		if (cb->select_cb) {
+			cb->select_cb(s->session, s->sep, caps, cb->user_data);
+			cb->select_cb = NULL;
+			setup_unref(s);
+		}
+	}
+
+	setup_unref(s);
+	return FALSE;
+}
+
 static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
 {
 	GSList *l;
@@ -276,6 +303,9 @@ static void stream_state_changed(struct avdtp_stream *stream,
 		sep->session = NULL;
 	}
 
+	if (sep->endpoint)
+		media_endpoint_clear_configuration(sep->endpoint);
+
 	sep->stream = NULL;
 
 }
@@ -505,6 +535,117 @@ static gboolean mpeg_getcap_ind(struct avdtp *session,
 	return TRUE;
 }
 
+static gboolean endpoint_setconf_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					struct avdtp_stream *stream,
+					GSList *caps, uint8_t *err,
+					uint8_t *category, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct audio_device *dev;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Set_Configuration_Ind", sep);
+	else
+		DBG("Source %p: Set_Configuration_Ind", sep);
+
+	dev = a2dp_get_dev(session);
+	if (!dev) {
+		*err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		*category = 0x00;
+		return FALSE;
+	}
+
+	for (; caps != NULL; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+		struct avdtp_media_codec_capability *codec;
+		gboolean ret;
+
+		if (cap->category == AVDTP_DELAY_REPORTING &&
+					!a2dp_sep->delay_reporting) {
+			*err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			*category = AVDTP_DELAY_REPORTING;
+			return FALSE;
+		}
+
+		if (cap->category != AVDTP_MEDIA_CODEC)
+			continue;
+
+		codec = (struct avdtp_media_codec_capability *) cap->data;
+
+		if (codec->media_codec_type != a2dp_sep->codec) {
+			*err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			*category = AVDTP_MEDIA_CODEC;
+			return FALSE;
+		}
+
+		ret = media_endpoint_set_configuration(a2dp_sep->endpoint, dev,
+					codec->data, cap->length - sizeof(*codec),
+					NULL, NULL);
+		if (ret)
+			break;
+
+		*err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		*category = AVDTP_MEDIA_CODEC;
+		return FALSE;
+	}
+
+	avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+	a2dp_sep->stream = stream;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+		sink_new_stream(dev, session, stream);
+
+	return TRUE;
+}
+
+static gboolean endpoint_getcap_ind(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				gboolean get_all,
+				GSList **caps, uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *codec_caps;
+	uint8_t *capabilities;
+	size_t length;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Get_Capability_Ind", sep);
+	else
+		DBG("Source %p: Get_Capability_Ind", sep);
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	length = media_endpoint_get_capabilities(a2dp_sep->endpoint,
+						&capabilities);
+
+	codec_caps = g_malloc0(sizeof(*codec_caps) + length);
+	codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec_caps->media_codec_type = a2dp_sep->codec;
+	memcpy(codec_caps->data, capabilities, length);
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+						sizeof(*codec_caps) + length);
+
+	*caps = g_slist_append(*caps, media_codec);
+	g_free(codec_caps);
+
+	if (get_all) {
+		struct avdtp_service_capability *delay_reporting;
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		*caps = g_slist_append(*caps, delay_reporting);
+	}
+
+	return TRUE;
+}
+
 static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 				struct avdtp_stream *stream,
 				struct avdtp_error *err, void *user_data)
@@ -543,6 +684,23 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	else
 		source_new_stream(dev, session, setup->stream);
 
+	/* Notify Endpoint */
+	if (a2dp_sep->endpoint) {
+		struct avdtp_service_capability *service;
+		struct avdtp_media_codec_capability *codec;
+
+		service = avdtp_stream_get_codec(stream);
+		codec = (struct avdtp_media_codec_capability *) service->data;
+
+		if (media_endpoint_set_configuration(a2dp_sep->endpoint, dev,
+						codec->data, service->length -
+						sizeof(*codec), NULL, NULL) ==
+						FALSE) {
+			setup->stream = NULL;
+			finalize_config_errno(setup, -EPERM);
+		}
+	}
+
 	ret = avdtp_open(session, stream);
 	if (ret < 0) {
 		error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
@@ -761,39 +919,11 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 static gboolean a2dp_reconfigure(gpointer data)
 {
 	struct a2dp_setup *setup = data;
-	struct avdtp_local_sep *lsep;
-	struct avdtp_remote_sep *rsep;
-	struct avdtp_service_capability *cap;
-	struct avdtp_media_codec_capability *codec_cap = NULL;
-	GSList *l;
+	struct a2dp_sep *sep = setup->sep;
 	int posix_err;
 
-	for (l = setup->client_caps; l != NULL; l = l->next) {
-		cap = l->data;
-
-		if (cap->category != AVDTP_MEDIA_CODEC)
-			continue;
-
-		codec_cap = (void *) cap->data;
-		break;
-	}
-
-	if (!codec_cap) {
-		error("Cannot find capabilities to reconfigure");
-		posix_err = -EINVAL;
-		goto failed;
-	}
-
-	posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK,
-					codec_cap->media_type,
-					codec_cap->media_codec_type,
-					&lsep, &rsep);
-	if (posix_err < 0) {
-		error("No matching ACP and INT SEPs found");
-		goto failed;
-	}
-
-	posix_err = avdtp_set_configuration(setup->session, rsep, lsep,
+	posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+						sep->lsep,
 						setup->client_caps,
 						&setup->stream);
 	if (posix_err < 0) {
@@ -975,6 +1105,19 @@ static struct avdtp_sep_ind mpeg_ind = {
 	.delayreport		= delayreport_ind,
 };
 
+static struct avdtp_sep_ind endpoint_ind = {
+	.get_capability		= endpoint_getcap_ind,
+	.set_configuration	= endpoint_setconf_ind,
+	.get_configuration	= getconf_ind,
+	.open			= open_ind,
+	.start			= start_ind,
+	.suspend		= suspend_ind,
+	.close			= close_ind,
+	.abort			= abort_ind,
+	.reconfigure		= reconf_ind,
+	.delayreport		= delayreport_ind,
+};
+
 static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
 {
 	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
@@ -1042,64 +1185,6 @@ static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
 	return record;
 }
 
-static struct a2dp_sep *a2dp_add_sep(struct a2dp_server *server, uint8_t type,
-					uint8_t codec, gboolean delay_reporting)
-{
-	struct a2dp_sep *sep;
-	GSList **l;
-	uint32_t *record_id;
-	sdp_record_t *record;
-	struct avdtp_sep_ind *ind;
-
-	sep = g_new0(struct a2dp_sep, 1);
-
-	ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
-	sep->sep = avdtp_register_sep(&server->src, type,
-					AVDTP_MEDIA_TYPE_AUDIO, codec,
-					delay_reporting, ind, &cfm, sep);
-	if (sep->sep == NULL) {
-		g_free(sep);
-		return NULL;
-	}
-
-	sep->codec = codec;
-	sep->type = type;
-	sep->delay_reporting = delay_reporting;
-
-	if (type == AVDTP_SEP_TYPE_SOURCE) {
-		l = &server->sources;
-		record_id = &server->source_record_id;
-	} else {
-		l = &server->sinks;
-		record_id = &server->sink_record_id;
-	}
-
-	if (*record_id != 0)
-		goto add;
-
-	record = a2dp_record(type, server->version);
-	if (!record) {
-		error("Unable to allocate new service record");
-		avdtp_unregister_sep(sep->sep);
-		g_free(sep);
-		return NULL;
-	}
-
-	if (add_record_to_server(&server->src, record) < 0) {
-		error("Unable to register A2DP service record");\
-		sdp_record_free(record);
-		avdtp_unregister_sep(sep->sep);
-		g_free(sep);
-		return NULL;
-	}
-	*record_id = record->handle;
-
-add:
-	*l = g_slist_append(*l, sep);
-
-	return sep;
-}
-
 static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
 {
 	GSList *l;
@@ -1220,22 +1305,23 @@ proceed:
 
 	if (source) {
 		for (i = 0; i < sbc_srcs; i++)
-			a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
-					A2DP_CODEC_SBC, delay_reporting);
+			a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+					A2DP_CODEC_SBC, delay_reporting, NULL);
 
 		for (i = 0; i < mpeg12_srcs; i++)
-			a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
-					A2DP_CODEC_MPEG12, delay_reporting);
+			a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+					A2DP_CODEC_MPEG12, delay_reporting, NULL);
 	}
 
 	if (sink) {
 		for (i = 0; i < sbc_sinks; i++)
-			a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
-					A2DP_CODEC_SBC, delay_reporting);
+			a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+					A2DP_CODEC_SBC, delay_reporting, NULL);
 
 		for (i = 0; i < mpeg12_sinks; i++)
-			a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
-					A2DP_CODEC_MPEG12, delay_reporting);
+			a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+					A2DP_CODEC_MPEG12, delay_reporting,
+					NULL);
 	}
 
 	return 0;
@@ -1243,7 +1329,7 @@ proceed:
 
 static void a2dp_unregister_sep(struct a2dp_sep *sep)
 {
-	avdtp_unregister_sep(sep->sep);
+	avdtp_unregister_sep(sep->lsep);
 	g_free(sep);
 }
 
@@ -1255,20 +1341,14 @@ void a2dp_unregister(const bdaddr_t *src)
 	if (!server)
 		return;
 
-	g_slist_foreach(server->sinks, (GFunc) a2dp_unregister_sep, NULL);
+	g_slist_foreach(server->sinks, (GFunc) a2dp_remove_sep, NULL);
 	g_slist_free(server->sinks);
 
-	g_slist_foreach(server->sources, (GFunc) a2dp_unregister_sep, NULL);
+	g_slist_foreach(server->sources, (GFunc) a2dp_remove_sep, NULL);
 	g_slist_free(server->sources);
 
 	avdtp_exit(src);
 
-	if (server->source_record_id)
-		remove_record_from_server(server->source_record_id);
-
-	if (server->sink_record_id)
-		remove_record_from_server(server->sink_record_id);
-
 	servers = g_slist_remove(servers, server);
 	g_free(server);
 
@@ -1279,6 +1359,100 @@ void a2dp_unregister(const bdaddr_t *src)
 	connection = NULL;
 }
 
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+				uint8_t codec, gboolean delay_reporting,
+				struct media_endpoint *endpoint)
+{
+	struct a2dp_server *server;
+	struct a2dp_sep *sep;
+	GSList **l;
+	uint32_t *record_id;
+	sdp_record_t *record;
+	struct avdtp_sep_ind *ind;
+
+	server = find_server(servers, src);
+	if (server == NULL)
+		return NULL;
+
+	sep = g_new0(struct a2dp_sep, 1);
+
+	if (endpoint) {
+		ind = &endpoint_ind;
+		goto proceed;
+	}
+
+	ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+
+proceed:
+	sep->lsep = avdtp_register_sep(&server->src, type,
+					AVDTP_MEDIA_TYPE_AUDIO, codec,
+					delay_reporting, ind, &cfm, sep);
+	if (sep->lsep == NULL) {
+		g_free(sep);
+		return NULL;
+	}
+
+	sep->server = server;
+	sep->endpoint = endpoint;
+	sep->codec = codec;
+	sep->type = type;
+	sep->delay_reporting = delay_reporting;
+
+	if (type == AVDTP_SEP_TYPE_SOURCE) {
+		l = &server->sources;
+		record_id = &server->source_record_id;
+	} else {
+		l = &server->sinks;
+		record_id = &server->sink_record_id;
+	}
+
+	if (*record_id != 0)
+		goto add;
+
+	record = a2dp_record(type, server->version);
+	if (!record) {
+		error("Unable to allocate new service record");
+		avdtp_unregister_sep(sep->lsep);
+		g_free(sep);
+		return NULL;
+	}
+
+	if (add_record_to_server(&server->src, record) < 0) {
+		error("Unable to register A2DP service record");\
+		sdp_record_free(record);
+		avdtp_unregister_sep(sep->lsep);
+		g_free(sep);
+		return NULL;
+	}
+	*record_id = record->handle;
+
+add:
+	*l = g_slist_append(*l, sep);
+
+	return sep;
+}
+
+void a2dp_remove_sep(struct a2dp_sep *sep)
+{
+	struct a2dp_server *server = sep->server;
+
+	if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+		server->sources = g_slist_remove(server->sources, sep);
+		if (server->sources == NULL && server->source_record_id) {
+			remove_record_from_server(server->source_record_id);
+			server->source_record_id = 0;
+		}
+	} else {
+		server->sinks = g_slist_remove(server->sinks, sep);
+		if (server->sinks == NULL && server->sink_record_id) {
+			remove_record_from_server(server->sink_record_id);
+			server->sink_record_id = 0;
+		}
+	}
+
+	a2dp_unregister_sep(sep);
+}
+
 struct a2dp_sep *a2dp_get(struct avdtp *session,
 				struct avdtp_remote_sep *rsep)
 {
@@ -1317,6 +1491,309 @@ struct a2dp_sep *a2dp_get(struct avdtp *session,
 	return NULL;
 }
 
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+	switch (freq) {
+	case SBC_SAMPLING_FREQ_16000:
+	case SBC_SAMPLING_FREQ_32000:
+		return 53;
+	case SBC_SAMPLING_FREQ_44100:
+		switch (mode) {
+		case SBC_CHANNEL_MODE_MONO:
+		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+			return 31;
+		case SBC_CHANNEL_MODE_STEREO:
+		case SBC_CHANNEL_MODE_JOINT_STEREO:
+			return 53;
+		default:
+			error("Invalid channel mode %u", mode);
+			return 53;
+		}
+	case SBC_SAMPLING_FREQ_48000:
+		switch (mode) {
+		case SBC_CHANNEL_MODE_MONO:
+		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+			return 29;
+		case SBC_CHANNEL_MODE_STEREO:
+		case SBC_CHANNEL_MODE_JOINT_STEREO:
+			return 51;
+		default:
+			error("Invalid channel mode %u", mode);
+			return 51;
+		}
+	default:
+		error("Invalid sampling freq %u", freq);
+		return 53;
+	}
+}
+
+static gboolean select_sbc_params(struct sbc_codec_cap *cap,
+					struct sbc_codec_cap *supported)
+{
+	unsigned int max_bitpool, min_bitpool;
+
+	memset(cap, 0, sizeof(struct sbc_codec_cap));
+
+	cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	cap->cap.media_codec_type = A2DP_CODEC_SBC;
+
+	if (supported->frequency & SBC_SAMPLING_FREQ_44100)
+		cap->frequency = SBC_SAMPLING_FREQ_44100;
+	else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
+		cap->frequency = SBC_SAMPLING_FREQ_48000;
+	else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
+		cap->frequency = SBC_SAMPLING_FREQ_32000;
+	else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
+		cap->frequency = SBC_SAMPLING_FREQ_16000;
+	else {
+		error("No supported frequencies");
+		return FALSE;
+	}
+
+	if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+		cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+	else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
+		cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
+	else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+		cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+	else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
+		cap->channel_mode = SBC_CHANNEL_MODE_MONO;
+	else {
+		error("No supported channel modes");
+		return FALSE;
+	}
+
+	if (supported->block_length & SBC_BLOCK_LENGTH_16)
+		cap->block_length = SBC_BLOCK_LENGTH_16;
+	else if (supported->block_length & SBC_BLOCK_LENGTH_12)
+		cap->block_length = SBC_BLOCK_LENGTH_12;
+	else if (supported->block_length & SBC_BLOCK_LENGTH_8)
+		cap->block_length = SBC_BLOCK_LENGTH_8;
+	else if (supported->block_length & SBC_BLOCK_LENGTH_4)
+		cap->block_length = SBC_BLOCK_LENGTH_4;
+	else {
+		error("No supported block lengths");
+		return FALSE;
+	}
+
+	if (supported->subbands & SBC_SUBBANDS_8)
+		cap->subbands = SBC_SUBBANDS_8;
+	else if (supported->subbands & SBC_SUBBANDS_4)
+		cap->subbands = SBC_SUBBANDS_4;
+	else {
+		error("No supported subbands");
+		return FALSE;
+	}
+
+	if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
+		cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
+	else if (supported->allocation_method & SBC_ALLOCATION_SNR)
+		cap->allocation_method = SBC_ALLOCATION_SNR;
+
+	min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
+	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+							supported->max_bitpool);
+
+	cap->min_bitpool = min_bitpool;
+	cap->max_bitpool = max_bitpool;
+
+	return TRUE;
+}
+
+static gboolean select_capabilities(struct avdtp *session,
+					struct avdtp_remote_sep *rsep,
+					GSList **caps)
+{
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct sbc_codec_cap sbc_cap;
+
+	media_codec = avdtp_get_codec(rsep);
+	if (!media_codec)
+		return FALSE;
+
+	select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+						sizeof(sbc_cap));
+
+	*caps = g_slist_append(*caps, media_codec);
+
+	if (avdtp_get_delay_reporting(rsep)) {
+		struct avdtp_service_capability *delay_reporting;
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		*caps = g_slist_append(*caps, delay_reporting);
+	}
+
+	return TRUE;
+}
+
+static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
+			void *user_data)
+{
+	struct a2dp_setup *setup = user_data;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *cap;
+	GSList *caps = NULL;
+
+	if (size < 0) {
+		DBG("Endpoint replied an invalid configuration");
+		goto done;
+	}
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	caps = g_slist_append(caps, media_transport);
+
+	cap = g_malloc0(sizeof(*cap) + size);
+	cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	cap->media_codec_type = setup->sep->codec;
+	memcpy(cap->data, ret, size);
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+						sizeof(*cap) + size);
+
+	caps = g_slist_append(caps, media_codec);
+
+done:
+	finalize_select(setup, caps);
+}
+
+static gboolean auto_select(gpointer data)
+{
+	struct a2dp_setup *setup = data;
+
+	finalize_select(setup, setup->client_caps);
+
+	return FALSE;
+}
+
+static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+					const char *sender)
+{
+	for (; list; list = list->next) {
+		struct a2dp_sep *sep = list->data;
+
+		/* Use sender's endpoint if available */
+		if (sender) {
+			const char *name;
+
+			if (sep->endpoint == NULL)
+				continue;
+
+			name = media_endpoint_get_sender(sep->endpoint);
+			if (g_strcmp0(sender, name) != 0)
+				continue;
+		}
+
+		if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+			continue;
+
+		return sep;
+	}
+
+	return NULL;
+}
+
+static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+					const char *sender)
+{
+	struct a2dp_server *server;
+	struct a2dp_sep *sep;
+	GSList *l;
+	bdaddr_t src;
+
+	avdtp_get_peers(session, &src, NULL);
+	server = find_server(servers, &src);
+	if (!server)
+		return NULL;
+
+	l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+	/* Check sender's seps first */
+	sep = a2dp_find_sep(session, l, sender);
+	if (sep != NULL)
+		return sep;
+
+	return a2dp_find_sep(session, l, NULL);
+}
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+					uint8_t type, const char *sender,
+					a2dp_select_cb_t cb,
+					void *user_data)
+{
+	struct a2dp_setup *setup;
+	struct a2dp_setup_cb *cb_data;
+	struct a2dp_sep *sep;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+
+	sep = a2dp_select_sep(session, type, sender);
+	if (!sep) {
+		error("Unable to select SEP");
+		return 0;
+	}
+
+	cb_data = g_new0(struct a2dp_setup_cb, 1);
+	cb_data->select_cb = cb;
+	cb_data->user_data = user_data;
+	cb_data->id = ++cb_id;
+
+	setup = find_setup_by_session(session);
+	if (!setup) {
+		setup = g_new0(struct a2dp_setup, 1);
+		setup->session = avdtp_ref(session);
+		setup->dev = a2dp_get_dev(session);
+		setups = g_slist_append(setups, setup);
+	}
+
+	setup_ref(setup);
+	setup->cb = g_slist_append(setup->cb, cb_data);
+	setup->sep = sep;
+	setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+
+	if (setup->rsep == NULL) {
+		error("Could not find remote sep");
+		goto fail;
+	}
+
+	/* FIXME: Remove auto select when it is not longer possible to register
+	endpoint in the configuration file */
+	if (sep->endpoint == NULL) {
+		if (!select_capabilities(session, setup->rsep,
+					&setup->client_caps)) {
+			error("Unable to auto select remote SEP capabilities");
+			goto fail;
+		}
+
+		g_idle_add(auto_select, setup);
+
+		return cb_data->id;
+	}
+
+	service = avdtp_get_codec(setup->rsep);
+	codec = (struct avdtp_media_codec_capability *) service->data;
+
+	if (media_endpoint_select_configuration(sep->endpoint, codec->data,
+						service->length - sizeof(*codec),
+						select_cb, setup) ==
+						TRUE)
+		return cb_data->id;
+
+fail:
+	setup_unref(setup);
+	cb_id--;
+	return 0;
+
+}
+
 unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 				a2dp_config_cb_t cb, GSList *caps,
 				void *user_data)
@@ -1326,8 +1803,6 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 	struct a2dp_server *server;
 	struct a2dp_setup *setup;
 	struct a2dp_sep *tmp;
-	struct avdtp_local_sep *lsep;
-	struct avdtp_remote_sep *rsep;
 	struct avdtp_service_capability *cap;
 	struct avdtp_media_codec_capability *codec_cap = NULL;
 	int posix_err;
@@ -1355,7 +1830,7 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 	if (sep->codec != codec_cap->media_codec_type)
 		return 0;
 
-	DBG("a2dp_config: selected SEP %p", sep->sep);
+	DBG("a2dp_config: selected SEP %p", sep->lsep);
 
 	cb_data = g_new0(struct a2dp_setup_cb, 1);
 	cb_data->config_cb = cb;
@@ -1376,7 +1851,7 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 	setup->stream = sep->stream;
 	setup->client_caps = caps;
 
-	switch (avdtp_sep_get_state(sep->sep)) {
+	switch (avdtp_sep_get_state(sep->lsep)) {
 	case AVDTP_STATE_IDLE:
 		if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
 			l = server->sources;
@@ -1403,16 +1878,15 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 			break;
 		}
 
-		if (avdtp_get_seps(session, remote_type,
-				codec_cap->media_type,
-				codec_cap->media_codec_type,
-				&lsep, &rsep) < 0) {
+		setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+		if (setup->rsep == NULL) {
 			error("No matching ACP and INT SEPs found");
 			goto failed;
 		}
 
-		posix_err = avdtp_set_configuration(session, rsep, lsep,
-							caps, &setup->stream);
+		posix_err = avdtp_set_configuration(session, setup->rsep,
+							sep->lsep, caps,
+							&setup->stream);
 		if (posix_err < 0) {
 			error("avdtp_set_configuration: %s",
 				strerror(-posix_err));
@@ -1469,7 +1943,7 @@ unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
 	setup->sep = sep;
 	setup->stream = sep->stream;
 
-	switch (avdtp_sep_get_state(sep->sep)) {
+	switch (avdtp_sep_get_state(sep->lsep)) {
 	case AVDTP_STATE_IDLE:
 		goto failed;
 		break;
@@ -1528,7 +2002,7 @@ unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
 	setup->sep = sep;
 	setup->stream = sep->stream;
 
-	switch (avdtp_sep_get_state(sep->sep)) {
+	switch (avdtp_sep_get_state(sep->lsep)) {
 	case AVDTP_STATE_IDLE:
 		error("a2dp_suspend: no stream to suspend");
 		goto failed;
@@ -1541,6 +2015,7 @@ unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
 			error("avdtp_suspend failed");
 			goto failed;
 		}
+		sep->suspending = TRUE;
 		break;
 	default:
 		error("SEP in bad state for suspend");
@@ -1595,7 +2070,7 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
 	if (sep->locked)
 		return FALSE;
 
-	DBG("SEP %p locked", sep->sep);
+	DBG("SEP %p locked", sep->lsep);
 	sep->locked = TRUE;
 
 	return TRUE;
@@ -1605,11 +2080,11 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
 {
 	avdtp_state_t state;
 
-	state = avdtp_sep_get_state(sep->sep);
+	state = avdtp_sep_get_state(sep->lsep);
 
 	sep->locked = FALSE;
 
-	DBG("SEP %p unlocked", sep->sep);
+	DBG("SEP %p unlocked", sep->lsep);
 
 	if (!sep->stream || state == AVDTP_STATE_IDLE)
 		return TRUE;
@@ -1671,3 +2146,8 @@ struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
 
 	return NULL;
 }
+
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep)
+{
+	return sep->stream;
+}
diff --git a/audio/a2dp.h b/audio/a2dp.h
index fa81776..21fccaa 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -121,6 +121,10 @@ struct mpeg_codec_cap {
 
 struct a2dp_sep;
 
+
+typedef void (*a2dp_select_cb_t) (struct avdtp *session,
+					struct a2dp_sep *sep, GSList *caps,
+					void *user_data);
 typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
 					struct avdtp_stream *stream,
 					struct avdtp_error *err,
@@ -132,7 +136,17 @@ typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
 int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
 void a2dp_unregister(const bdaddr_t *src);
 
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+				uint8_t codec, gboolean delay_reporting,
+				struct media_endpoint *endpoint);
+void a2dp_remove_sep(struct a2dp_sep *sep);
+
 struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep);
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+					uint8_t type, const char *sender,
+					a2dp_select_cb_t cb,
+					void *user_data);
 unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
 				a2dp_config_cb_t cb, GSList *caps,
 				void *user_data);
@@ -145,5 +159,6 @@ gboolean a2dp_cancel(struct audio_device *dev, unsigned int id);
 gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
 gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
 gboolean a2dp_sep_get_lock(struct a2dp_sep *sep);
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
 struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
 				struct avdtp_stream *stream);
diff --git a/audio/avdtp.c b/audio/avdtp.c
index cc7066f..b9a03f8 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -1191,22 +1191,36 @@ static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *serve
 	return NULL;
 }
 
-static struct avdtp_local_sep *find_local_sep(struct avdtp_server *server,
-						uint8_t type,
-						uint8_t media_type,
-						uint8_t codec)
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep)
 {
 	GSList *l;
 
-	for (l = server->seps; l != NULL; l = g_slist_next(l)) {
-		struct avdtp_local_sep *sep = l->data;
+	if (lsep->info.inuse)
+		return NULL;
+
+	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+		struct avdtp_service_capability *cap;
+		struct avdtp_media_codec_capability *codec_data;
+
+		/* Type must be different: source <-> sink */
+		if (sep->type == lsep->info.type)
+			continue;
+
+		if (sep->media_type != lsep->info.media_type)
+			continue;
 
-		if (sep->info.inuse)
+		if (!sep->codec)
 			continue;
 
-		if (sep->info.type == type &&
-				sep->info.media_type == media_type &&
-				sep->codec == codec)
+		cap = sep->codec;
+		codec_data = (void *) cap->data;
+
+		if (codec_data->media_codec_type != lsep->codec)
+			continue;
+
+		if (sep->stream == NULL)
 			return sep;
 	}
 
@@ -3214,49 +3228,6 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
 	return err;
 }
 
-int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type,
-			uint8_t codec, struct avdtp_local_sep **lsep,
-			struct avdtp_remote_sep **rsep)
-{
-	GSList *l;
-	uint8_t int_type;
-
-	int_type = acp_type == AVDTP_SEP_TYPE_SINK ?
-				AVDTP_SEP_TYPE_SOURCE : AVDTP_SEP_TYPE_SINK;
-
-	*lsep = find_local_sep(session->server, int_type, media_type, codec);
-	if (!*lsep)
-		return -EINVAL;
-
-	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
-		struct avdtp_remote_sep *sep = l->data;
-		struct avdtp_service_capability *cap;
-		struct avdtp_media_codec_capability *codec_data;
-
-		if (sep->type != acp_type)
-			continue;
-
-		if (sep->media_type != media_type)
-			continue;
-
-		if (!sep->codec)
-			continue;
-
-		cap = sep->codec;
-		codec_data = (void *) cap->data;
-
-		if (codec_data->media_codec_type != codec)
-			continue;
-
-		if (!sep->stream) {
-			*rsep = sep;
-			return 0;
-		}
-	}
-
-	return -EINVAL;
-}
-
 gboolean avdtp_stream_remove_cb(struct avdtp *session,
 				struct avdtp_stream *stream,
 				unsigned int id)
@@ -3354,8 +3325,14 @@ int avdtp_set_configuration(struct avdtp *session,
 	new_stream->lsep = lsep;
 	new_stream->rseid = rsep->seid;
 
-	if (rsep->delay_reporting && lsep->delay_reporting)
+	if (rsep->delay_reporting && lsep->delay_reporting) {
+		struct avdtp_service_capability *delay_reporting;
+
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		caps = g_slist_append(caps, delay_reporting);
 		new_stream->delay_reporting = TRUE;
+	}
 
 	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
 
@@ -3625,6 +3602,9 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep)
 	if (sep->stream)
 		release_stream(sep->stream, sep->stream->session);
 
+	DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
 	g_free(sep);
 
 	return 0;
diff --git a/audio/avdtp.h b/audio/avdtp.h
index 3fe682b..bf10a2f 100644
--- a/audio/avdtp.h
+++ b/audio/avdtp.h
@@ -290,9 +290,8 @@ struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
 						void *user_data);
 
 /* Find a matching pair of local and remote SEP ID's */
-int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media,
-			uint8_t codec, struct avdtp_local_sep **lsep,
-			struct avdtp_remote_sep **rsep);
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep);
 
 int avdtp_unregister_sep(struct avdtp_local_sep *sep);
 
diff --git a/audio/manager.c b/audio/manager.c
index 87e7a2a..816c807 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -61,6 +61,7 @@
 #include "device.h"
 #include "error.h"
 #include "avdtp.h"
+#include "media.h"
 #include "a2dp.h"
 #include "headset.h"
 #include "gateway.h"
@@ -72,6 +73,10 @@
 #include "telephony.h"
 #include "unix.h"
 
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
 typedef enum {
 	HEADSET	= 1 << 0,
 	GATEWAY	= 1 << 1,
@@ -115,6 +120,7 @@ static struct enabled_interfaces enabled = {
 	.source		= FALSE,
 	.control	= TRUE,
 	.socket		= TRUE,
+	.media		= FALSE
 };
 
 static struct audio_adapter *find_adapter(GSList *list,
@@ -1015,6 +1021,38 @@ static void avrcp_server_remove(struct btd_adapter *adapter)
 	audio_adapter_unref(adp);
 }
 
+static int media_server_probe(struct btd_adapter *adapter)
+{
+	struct audio_adapter *adp;
+	const gchar *path = adapter_get_path(adapter);
+	bdaddr_t src;
+
+	DBG("path %s", path);
+
+	adp = audio_adapter_get(adapter);
+	if (!adp)
+		return -EINVAL;
+
+	adapter_get_address(adapter, &src);
+
+	return media_register(connection, path, &src);
+}
+
+static void media_server_remove(struct btd_adapter *adapter)
+{
+	struct audio_adapter *adp;
+	const gchar *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	adp = find_adapter(adapters, adapter);
+	if (!adp)
+		return;
+
+	media_unregister(path);
+	audio_adapter_unref(adp);
+}
+
 static struct btd_device_driver audio_driver = {
 	.name	= "audio",
 	.uuids	= BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
@@ -1048,6 +1086,12 @@ static struct btd_adapter_driver avrcp_server_driver = {
 	.remove	= avrcp_server_remove,
 };
 
+static struct btd_adapter_driver media_server_driver = {
+	.name	= "media",
+	.probe	= media_server_probe,
+	.remove	= media_server_remove,
+};
+
 int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 							gboolean *enable_sco)
 {
@@ -1078,6 +1122,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 			enabled.control = TRUE;
 		else if (g_str_equal(list[i], "Socket"))
 			enabled.socket = TRUE;
+		else if (g_str_equal(list[i], "Media"))
+			enabled.media = TRUE;
 	}
 	g_strfreev(list);
 
@@ -1096,6 +1142,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
 			enabled.control = FALSE;
 		else if (g_str_equal(list[i], "Socket"))
 			enabled.socket = FALSE;
+		else if (g_str_equal(list[i], "Media"))
+			enabled.media = FALSE;
 	}
 	g_strfreev(list);
 
@@ -1126,6 +1174,9 @@ proceed:
 	if (enabled.socket)
 		unix_init();
 
+	if (enabled.media)
+		btd_register_adapter_driver(&media_server_driver);
+
 	if (enabled.headset) {
 		telephony_init();
 		btd_register_adapter_driver(&headset_server_driver);
@@ -1164,6 +1215,9 @@ void audio_manager_exit(void)
 	if (enabled.socket)
 		unix_exit();
 
+	if (enabled.media)
+		btd_unregister_adapter_driver(&media_server_driver);
+
 	if (enabled.headset) {
 		btd_unregister_adapter_driver(&headset_server_driver);
 		telephony_exit();
diff --git a/audio/manager.h b/audio/manager.h
index c79b761..0bf7663 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -30,6 +30,7 @@ struct enabled_interfaces {
 	gboolean source;
 	gboolean control;
 	gboolean socket;
+	gboolean media;
 };
 
 int audio_manager_init(DBusConnection *conn, GKeyFile *config,
diff --git a/audio/media.c b/audio/media.c
new file mode 100644
index 0000000..da9d3ae
--- /dev/null
+++ b/audio/media.c
@@ -0,0 +1,683 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define MEDIA_INTERFACE "org.bluez.Media"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+
+#define REQUEST_TIMEOUT (3 * 1000)		/* 3 seconds */
+
+struct media_adapter {
+	bdaddr_t		src;		/* Adapter address */
+	char			*path;		/* Adapter path */
+	DBusConnection		*conn;		/* Adapter connection */
+	GSList			*endpoints;	/* Endpoints list */
+};
+
+struct endpoint_request {
+	DBusMessage		*msg;
+	DBusPendingCall		*call;
+	media_endpoint_cb_t	cb;
+	void			*user_data;
+};
+
+struct media_endpoint {
+	struct a2dp_sep		*sep;
+	char			*sender;	/* Endpoint DBus bus id */
+	char			*path;		/* Endpoint object path */
+	char			*uuid;		/* Endpoint property UUID */
+	uint8_t			codec;		/* Endpoint codec */
+	uint8_t			*capabilities;	/* Endpoint property capabilities */
+	size_t			size;		/* Endpoint capabilities size */
+	guint			hs_watch;
+	guint			watch;
+	struct endpoint_request *request;
+	struct media_transport	*transport;
+	struct media_adapter	*adapter;
+};
+
+static GSList *adapters = NULL;
+
+static void endpoint_request_free(struct endpoint_request *request)
+{
+	if (request->call)
+		dbus_pending_call_cancel(request->call);
+
+	dbus_message_unref(request->msg);
+	g_free(request);
+}
+
+static void media_endpoint_remove(struct media_endpoint *endpoint)
+{
+	struct media_adapter *adapter = endpoint->adapter;
+
+	info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
+			endpoint->path);
+
+	adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
+
+	if (endpoint->sep)
+		a2dp_remove_sep(endpoint->sep);
+
+	if (endpoint->hs_watch)
+		headset_remove_state_cb(endpoint->hs_watch);
+
+	if (endpoint->request)
+		endpoint_request_free(endpoint->request);
+
+	if (endpoint->transport)
+		media_transport_remove(endpoint->transport);
+
+	g_dbus_remove_watch(adapter->conn, endpoint->watch);
+	g_free(endpoint->capabilities);
+	g_free(endpoint->sender);
+	g_free(endpoint->path);
+	g_free(endpoint->uuid);
+	g_free(endpoint);
+}
+
+static void media_endpoint_exit(DBusConnection *connection, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	endpoint->watch = 0;
+	media_endpoint_remove(endpoint);
+}
+
+static void headset_state_changed(struct audio_device *dev,
+					headset_state_t old_state,
+					headset_state_t new_state,
+					void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	DBG("");
+
+	switch (new_state) {
+	case HEADSET_STATE_DISCONNECTED:
+		if (old_state != HEADSET_STATE_CONNECTING)
+			media_endpoint_clear_configuration(endpoint);
+	case HEADSET_STATE_CONNECTING:
+		break;
+	case HEADSET_STATE_CONNECTED:
+		if (old_state != HEADSET_STATE_PLAY_IN_PROGRESS &&
+				old_state != HEADSET_STATE_PLAYING)
+			media_endpoint_set_configuration(endpoint, dev, NULL,
+								0, NULL, NULL);
+		break;
+	case HEADSET_STATE_PLAY_IN_PROGRESS:
+		break;
+	case HEADSET_STATE_PLAYING:
+		break;
+	}
+}
+
+static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
+						const char *sender,
+						const char *path,
+						const char *uuid,
+						gboolean delay_reporting,
+						uint8_t codec,
+						uint8_t *capabilities,
+						int size)
+{
+	struct media_endpoint *endpoint;
+
+	endpoint = g_new0(struct media_endpoint, 1);
+	endpoint->sender = g_strdup(sender);
+	endpoint->path = g_strdup(path);
+	endpoint->uuid = g_strdup(uuid);
+	endpoint->codec = codec;
+	endpoint->capabilities = g_new(uint8_t, size);
+	memcpy(endpoint->capabilities, capabilities, size);
+	endpoint->size = size;
+	endpoint->adapter = adapter;
+
+	if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0) {
+		endpoint->sep = a2dp_add_sep(&adapter->src,
+					AVDTP_SEP_TYPE_SOURCE, codec,
+					delay_reporting, endpoint);
+		if (endpoint->sep == NULL)
+			goto failed;
+	} else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+		endpoint->sep = a2dp_add_sep(&adapter->src,
+						AVDTP_SEP_TYPE_SINK, codec,
+						delay_reporting, endpoint);
+		if (endpoint->sep == NULL)
+			goto failed;
+	} else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
+					g_strcmp0(uuid, HSP_AG_UUID) == 0)
+		endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
+								endpoint);
+	else
+		goto failed;
+
+	endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+						media_endpoint_exit, endpoint,
+						NULL);
+
+	adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
+	info("Endpoint registered: sender=%s path=%s", sender, path);
+
+	return endpoint;
+
+failed:
+	g_free(endpoint);
+	return NULL;
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+						struct media_adapter *adapter,
+						const char *sender,
+						const char *path,
+						const char *uuid)
+{
+	GSList *l;
+
+	for (l = adapter->endpoints; l; l = l->next) {
+		struct media_endpoint *endpoint = l->data;
+
+		if (sender && g_strcmp0(endpoint->sender, sender) != 0)
+			continue;
+
+		if (path && g_strcmp0(endpoint->path, path) != 0)
+			continue;
+
+		if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
+			continue;
+
+		return endpoint;
+	}
+
+	return NULL;
+}
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
+{
+	return endpoint->sender;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+				gboolean *delay_reporting, uint8_t *codec,
+				uint8_t **capabilities, int *size)
+{
+	while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+		int var;
+
+		dbus_message_iter_recurse(props, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		var = dbus_message_iter_get_arg_type(&value);
+		if (strcasecmp(key, "UUID") == 0) {
+			if (var != DBUS_TYPE_STRING)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, uuid);
+		} else if (strcasecmp(key, "Codec") == 0) {
+			if (var != DBUS_TYPE_BYTE)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, codec);
+		} else if (strcasecmp(key, "DelayReporting") == 0) {
+			if (var != DBUS_TYPE_BOOLEAN)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, delay_reporting);
+		} else if (strcasecmp(key, "Capabilities") == 0) {
+			DBusMessageIter array;
+
+			if (var != DBUS_TYPE_ARRAY)
+				return -EINVAL;
+
+			dbus_message_iter_recurse(&value, &array);
+			dbus_message_iter_get_fixed_array(&array, capabilities,
+							size);
+		}
+
+		dbus_message_iter_next(props);
+	}
+
+	return 0;
+}
+
+static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	DBusMessageIter args, props;
+	const char *sender, *path, *uuid = NULL;
+	gboolean delay_reporting;
+	uint8_t codec;
+	uint8_t *capabilities;
+	int size;
+
+	sender = dbus_message_get_sender(msg);
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
+		return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+				"Endpoint already registered");
+
+	dbus_message_iter_recurse(&args, &props);
+	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+					".Failed", "Invalid argument");
+
+	if (parse_properties(&props, &uuid, &delay_reporting, &codec,
+				&capabilities, &size) || uuid == NULL)
+		return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+						"Invalid argument");
+
+	if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+				codec, capabilities, size) == FALSE)
+		return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+						"Invalid argument");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	struct media_endpoint *endpoint;
+	const char *sender, *path;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	sender = dbus_message_get_sender(msg);
+
+	endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
+	if (endpoint == NULL)
+		return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+				"Endpoint not registered");
+
+	media_endpoint_remove(endpoint);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable media_methods[] = {
+	{ "RegisterEndpoint",	"oa{sv}",	"",	register_endpoint },
+	{ "UnregisterEndpoint",	"o",		"",	unregister_endpoint },
+	{ },
+};
+
+static void path_free(void *data)
+{
+	struct media_adapter *adapter = data;
+
+	g_slist_foreach(adapter->endpoints, (GFunc) media_endpoint_release,
+									NULL);
+	g_slist_free(adapter->endpoints);
+
+	dbus_connection_unref(adapter->conn);
+
+	adapters = g_slist_remove(adapters, adapter);
+
+	g_free(adapter->path);
+	g_free(adapter);
+}
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
+{
+	struct media_adapter *adapter;
+
+	if (DBUS_TYPE_UNIX_FD < 0)
+		return -EPERM;
+
+	adapter = g_new0(struct media_adapter, 1);
+	adapter->conn = dbus_connection_ref(conn);
+	bacpy(&adapter->src, src);
+	adapter->path = g_strdup(path);
+
+	if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
+					media_methods, NULL, NULL,
+					adapter, path_free)) {
+		error("D-Bus failed to register %s path", path);
+		path_free(adapter);
+		return -1;
+	}
+
+	adapters = g_slist_append(adapters, adapter);
+
+	return 0;
+}
+
+void media_unregister(const char *path)
+{
+	GSList *l;
+
+	for (l = adapters; l; l = l->next) {
+		struct media_adapter *adapter = l->data;
+
+		if (g_strcmp0(path, adapter->path) == 0) {
+			g_dbus_unregister_interface(adapter->conn, path,
+							MEDIA_INTERFACE);
+			return;
+		}
+	}
+}
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+					uint8_t **capabilities)
+{
+	*capabilities = endpoint->capabilities;
+	return endpoint->size;
+}
+
+static void endpoint_reply(DBusPendingCall *call, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+	struct endpoint_request *request = endpoint->request;
+	DBusMessage *reply;
+	DBusError err;
+	gboolean value;
+	void *ret = NULL;
+	int size = -1;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, reply)) {
+		error("Endpoint replied with an error: %s",
+				err.name);
+
+		/* Clear endpoint configuration in case of NO_REPLY error */
+		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+			media_endpoint_clear_configuration(endpoint);
+			dbus_message_unref(reply);
+			dbus_error_free(&err);
+			return;
+		}
+
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
+				"SelectConfiguration")) {
+		DBusMessageIter args, array;
+		uint8_t *configuration;
+
+		dbus_message_iter_init(reply, &args);
+
+		dbus_message_iter_recurse(&args, &array);
+
+		dbus_message_iter_get_fixed_array(&array, &configuration, &size);
+
+		ret = configuration;
+		goto done;
+	} else  if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+		error("Wrong reply signature: %s", err.message);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	size = 1;
+	value = TRUE;
+	ret = &value;
+
+done:
+	dbus_message_unref(reply);
+
+	request->cb(endpoint, ret, size, request->user_data);
+
+	endpoint_request_free(request);
+	endpoint->request = NULL;
+}
+
+static gboolean media_endpoint_async_call(DBusConnection *conn,
+					DBusMessage *msg,
+					struct media_endpoint *endpoint,
+					media_endpoint_cb_t cb,
+					void *user_data)
+{
+	struct endpoint_request *request;
+
+	if (endpoint->request)
+		return FALSE;
+
+	request = g_new0(struct endpoint_request, 1);
+
+	/* Timeout should be less than avdtp request timeout (4 seconds) */
+	if (dbus_connection_send_with_reply(conn, msg, &request->call,
+						REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		g_free(request);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
+
+	request->msg = msg;
+	request->cb = cb;
+	request->user_data = user_data;
+	endpoint->request = request;
+
+	DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+			dbus_message_get_destination(msg),
+			dbus_message_get_path(msg));
+
+	return TRUE;
+}
+
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+					struct audio_device *device,
+					uint8_t *configuration, size_t size,
+					media_endpoint_cb_t cb,
+					void *user_data)
+{
+	DBusConnection *conn;
+	DBusMessage *msg, *reply;
+	DBusError err;
+	const char *path;
+
+	if (endpoint->transport != NULL || endpoint->request != NULL)
+		return FALSE;
+
+	conn = endpoint->adapter->conn;
+
+	endpoint->transport = media_transport_create(conn, endpoint, device,
+						configuration, size);
+	if (endpoint->transport == NULL)
+		return FALSE;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"SetConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return FALSE;
+	}
+
+	path = media_transport_get_path(endpoint->transport);
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+					&configuration, size,
+					DBUS_TYPE_INVALID);
+
+	if (cb != NULL)
+		return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+
+	dbus_error_init(&err);
+
+	DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+			dbus_message_get_destination(msg),
+			dbus_message_get_path(msg));
+
+	/* FIXME: remove once we can reply setconf asynchronously */
+	reply = dbus_connection_send_with_reply_and_block(conn, msg,
+							REQUEST_TIMEOUT, &err);
+
+	dbus_message_unref(msg);
+
+	if (reply) {
+		dbus_message_unref(reply);
+		return TRUE;
+	}
+
+	if (dbus_error_is_set(&err)) {
+		error("Endpoint replied with an error: %s", err.name);
+
+		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY))
+			media_endpoint_clear_configuration(endpoint);
+
+		dbus_error_free(&err);
+	}
+
+	return FALSE;
+}
+
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+						uint8_t *capabilities,
+						size_t length,
+						media_endpoint_cb_t cb,
+						void *user_data)
+{
+	DBusConnection *conn;
+	DBusMessage *msg;
+
+	if (endpoint->request != NULL)
+		return FALSE;
+
+	conn = endpoint->adapter->conn;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"SelectConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return FALSE;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+					&capabilities, length,
+					DBUS_TYPE_INVALID);
+
+	return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+}
+
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint)
+{
+	DBusConnection *conn;
+	DBusMessage *msg;
+
+	if (endpoint->transport == NULL)
+		return;
+
+	if (endpoint->request) {
+		endpoint_request_free(endpoint->request);
+		endpoint->request = NULL;
+	}
+
+	conn = endpoint->adapter->conn;
+
+	media_transport_remove(endpoint->transport);
+	endpoint->transport = NULL;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"ClearConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(conn, msg);
+}
+
+void media_endpoint_release(struct media_endpoint *endpoint)
+{
+	DBusMessage *msg;
+
+	DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+	/* already exit */
+	if (endpoint->watch == 0)
+		return;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"Release");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(endpoint->adapter->conn, msg);
+
+	media_endpoint_remove(endpoint);
+}
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
+{
+	return endpoint->sep;
+}
+
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
+{
+	return endpoint->uuid;
+}
+
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
+{
+	return endpoint->codec;
+}
diff --git a/audio/media.h b/audio/media.h
new file mode 100644
index 0000000..f6d144c
--- /dev/null
+++ b/audio/media.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct media_endpoint;
+
+typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint,
+					void *ret, int size, void *user_data);
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src);
+void media_unregister(const char *path);
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint);
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+					uint8_t **capabilities);
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+					struct audio_device *device,
+					uint8_t *configuration, size_t size,
+					media_endpoint_cb_t cb,
+					void *user_data);
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+						uint8_t *capabilities,
+						size_t length,
+						media_endpoint_cb_t cb,
+						void *user_data);
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint);
+void media_endpoint_release(struct media_endpoint *endpoint);
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
diff --git a/audio/sink.c b/audio/sink.c
index f4dce28..67cffee 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -40,6 +40,7 @@
 
 #include "device.h"
 #include "avdtp.h"
+#include "media.h"
 #include "a2dp.h"
 #include "error.h"
 #include "sink.h"
@@ -343,146 +344,30 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
 	}
 }
 
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+			GSList *caps, void *user_data)
 {
-	switch (freq) {
-	case SBC_SAMPLING_FREQ_16000:
-	case SBC_SAMPLING_FREQ_32000:
-		return 53;
-	case SBC_SAMPLING_FREQ_44100:
-		switch (mode) {
-		case SBC_CHANNEL_MODE_MONO:
-		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-			return 31;
-		case SBC_CHANNEL_MODE_STEREO:
-		case SBC_CHANNEL_MODE_JOINT_STEREO:
-			return 53;
-		default:
-			error("Invalid channel mode %u", mode);
-			return 53;
-		}
-	case SBC_SAMPLING_FREQ_48000:
-		switch (mode) {
-		case SBC_CHANNEL_MODE_MONO:
-		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-			return 29;
-		case SBC_CHANNEL_MODE_STEREO:
-		case SBC_CHANNEL_MODE_JOINT_STEREO:
-			return 51;
-		default:
-			error("Invalid channel mode %u", mode);
-			return 51;
-		}
-	default:
-		error("Invalid sampling freq %u", freq);
-		return 53;
-	}
-}
-
-static gboolean select_sbc_params(struct sbc_codec_cap *cap,
-					struct sbc_codec_cap *supported)
-{
-	unsigned int max_bitpool, min_bitpool;
-
-	memset(cap, 0, sizeof(struct sbc_codec_cap));
-
-	cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
-	cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
-	if (supported->frequency & SBC_SAMPLING_FREQ_44100)
-		cap->frequency = SBC_SAMPLING_FREQ_44100;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
-		cap->frequency = SBC_SAMPLING_FREQ_48000;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
-		cap->frequency = SBC_SAMPLING_FREQ_32000;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
-		cap->frequency = SBC_SAMPLING_FREQ_16000;
-	else {
-		error("No supported frequencies");
-		return FALSE;
-	}
-
-	if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
-		cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
-		cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
-		cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
-		cap->channel_mode = SBC_CHANNEL_MODE_MONO;
-	else {
-		error("No supported channel modes");
-		return FALSE;
-	}
-
-	if (supported->block_length & SBC_BLOCK_LENGTH_16)
-		cap->block_length = SBC_BLOCK_LENGTH_16;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_12)
-		cap->block_length = SBC_BLOCK_LENGTH_12;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_8)
-		cap->block_length = SBC_BLOCK_LENGTH_8;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_4)
-		cap->block_length = SBC_BLOCK_LENGTH_4;
-	else {
-		error("No supported block lengths");
-		return FALSE;
-	}
-
-	if (supported->subbands & SBC_SUBBANDS_8)
-		cap->subbands = SBC_SUBBANDS_8;
-	else if (supported->subbands & SBC_SUBBANDS_4)
-		cap->subbands = SBC_SUBBANDS_4;
-	else {
-		error("No supported subbands");
-		return FALSE;
-	}
-
-	if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
-		cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
-	else if (supported->allocation_method & SBC_ALLOCATION_SNR)
-		cap->allocation_method = SBC_ALLOCATION_SNR;
-
-	min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
-	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
-							supported->max_bitpool);
-
-	cap->min_bitpool = min_bitpool;
-	cap->max_bitpool = max_bitpool;
-
-	return TRUE;
-}
-
-static gboolean select_capabilities(struct avdtp *session,
-					struct avdtp_remote_sep *rsep,
-					GSList **caps)
-{
-	struct avdtp_service_capability *media_transport, *media_codec;
-	struct sbc_codec_cap sbc_cap;
-
-	media_codec = avdtp_get_codec(rsep);
-	if (!media_codec)
-		return FALSE;
-
-	select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
-
-	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
-						NULL, 0);
-
-	*caps = g_slist_append(*caps, media_transport);
+	struct sink *sink = user_data;
+	struct pending_request *pending;
+	int id;
 
-	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
-						sizeof(sbc_cap));
+	pending = sink->connect;
+	pending->id = 0;
 
-	*caps = g_slist_append(*caps, media_codec);
+	id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
+	if (id == 0)
+		goto failed;
 
-	if (avdtp_get_delay_reporting(rsep)) {
-		struct avdtp_service_capability *delay_reporting;
-		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
-								NULL, 0);
-		*caps = g_slist_append(*caps, delay_reporting);
-	}
+	pending->id = id;
+	return;
 
-	return TRUE;
+failed:
+	if (pending->msg)
+		error_failed(pending->conn, pending->msg, "Stream setup failed");
+	pending_request_free(sink->dev, pending);
+	sink->connect = NULL;
+	avdtp_unref(sink->session);
+	sink->session = NULL;
 }
 
 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
@@ -490,10 +375,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
 {
 	struct sink *sink = user_data;
 	struct pending_request *pending;
-	struct avdtp_local_sep *lsep;
-	struct avdtp_remote_sep *rsep;
-	struct a2dp_sep *sep;
-	GSList *caps = NULL;
 	int id;
 
 	pending = sink->connect;
@@ -515,24 +396,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
 
 	DBG("Discovery complete");
 
-	if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-				A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
-		error("No matching ACP and INT SEPs found");
-		goto failed;
-	}
-
-	if (!select_capabilities(session, rsep, &caps)) {
-		error("Unable to select remote SEP capabilities");
-		goto failed;
-	}
-
-	sep = a2dp_get(session, rsep);
-	if (!sep) {
-		error("Unable to get a local source SEP");
-		goto failed;
-	}
-
-	id = a2dp_config(sink->session, sep, stream_setup_complete, caps, sink);
+	id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
+						select_complete, sink);
 	if (id == 0)
 		goto failed;
 
diff --git a/audio/source.c b/audio/source.c
index 35d8136..01173f5 100644
--- a/audio/source.c
+++ b/audio/source.c
@@ -41,6 +41,7 @@
 
 #include "device.h"
 #include "avdtp.h"
+#include "media.h"
 #include "a2dp.h"
 #include "error.h"
 #include "source.h"
@@ -310,140 +311,34 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
 	}
 }
 
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+			GSList *caps, void *user_data)
 {
-	switch (freq) {
-	case SBC_SAMPLING_FREQ_16000:
-	case SBC_SAMPLING_FREQ_32000:
-		return 53;
-	case SBC_SAMPLING_FREQ_44100:
-		switch (mode) {
-		case SBC_CHANNEL_MODE_MONO:
-		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-			return 31;
-		case SBC_CHANNEL_MODE_STEREO:
-		case SBC_CHANNEL_MODE_JOINT_STEREO:
-			return 53;
-		default:
-			error("Invalid channel mode %u", mode);
-			return 53;
-		}
-	case SBC_SAMPLING_FREQ_48000:
-		switch (mode) {
-		case SBC_CHANNEL_MODE_MONO:
-		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-			return 29;
-		case SBC_CHANNEL_MODE_STEREO:
-		case SBC_CHANNEL_MODE_JOINT_STEREO:
-			return 51;
-		default:
-			error("Invalid channel mode %u", mode);
-			return 51;
-		}
-	default:
-		error("Invalid sampling freq %u", freq);
-		return 53;
-	}
-}
-
-static gboolean select_sbc_params(struct sbc_codec_cap *cap,
-					struct sbc_codec_cap *supported)
-{
-	unsigned int max_bitpool, min_bitpool;
-
-	memset(cap, 0, sizeof(struct sbc_codec_cap));
-
-	cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
-	cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
-	if (supported->frequency & SBC_SAMPLING_FREQ_44100)
-		cap->frequency = SBC_SAMPLING_FREQ_44100;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
-		cap->frequency = SBC_SAMPLING_FREQ_48000;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
-		cap->frequency = SBC_SAMPLING_FREQ_32000;
-	else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
-		cap->frequency = SBC_SAMPLING_FREQ_16000;
-	else {
-		error("No supported frequencies");
-		return FALSE;
-	}
-
-	if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
-		cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
-		cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
-		cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
-	else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
-		cap->channel_mode = SBC_CHANNEL_MODE_MONO;
-	else {
-		error("No supported channel modes");
-		return FALSE;
-	}
-
-	if (supported->block_length & SBC_BLOCK_LENGTH_16)
-		cap->block_length = SBC_BLOCK_LENGTH_16;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_12)
-		cap->block_length = SBC_BLOCK_LENGTH_12;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_8)
-		cap->block_length = SBC_BLOCK_LENGTH_8;
-	else if (supported->block_length & SBC_BLOCK_LENGTH_4)
-		cap->block_length = SBC_BLOCK_LENGTH_4;
-	else {
-		error("No supported block lengths");
-		return FALSE;
-	}
-
-	if (supported->subbands & SBC_SUBBANDS_8)
-		cap->subbands = SBC_SUBBANDS_8;
-	else if (supported->subbands & SBC_SUBBANDS_4)
-		cap->subbands = SBC_SUBBANDS_4;
-	else {
-		error("No supported subbands");
-		return FALSE;
-	}
-
-	if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
-		cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
-	else if (supported->allocation_method & SBC_ALLOCATION_SNR)
-		cap->allocation_method = SBC_ALLOCATION_SNR;
-
-	min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
-	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
-							supported->max_bitpool);
-
-	cap->min_bitpool = min_bitpool;
-	cap->max_bitpool = max_bitpool;
-
-	return TRUE;
-}
-
-static gboolean select_capabilities(struct avdtp *session,
-					struct avdtp_remote_sep *rsep,
-					GSList **caps)
-{
-	struct avdtp_service_capability *media_transport, *media_codec;
-	struct sbc_codec_cap sbc_cap;
-
-	media_codec = avdtp_get_codec(rsep);
-	if (!media_codec)
-		return FALSE;
-
-	select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+	struct source *source = user_data;
+	struct pending_request *pending;
+	int id;
 
-	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
-						NULL, 0);
+	pending = source->connect;
 
-	*caps = g_slist_append(*caps, media_transport);
+	pending->id = 0;
 
-	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
-						sizeof(sbc_cap));
+	if (caps == NULL)
+		goto failed;
 
-	*caps = g_slist_append(*caps, media_codec);
+	id = a2dp_config(session, sep, stream_setup_complete, caps, source);
+	if (id == 0)
+		goto failed;
 
+	pending->id = id;
+	return;
 
-	return TRUE;
+failed:
+	if (pending->msg)
+		error_failed(pending->conn, pending->msg, "Stream setup failed");
+	pending_request_free(source->dev, pending);
+	source->connect = NULL;
+	avdtp_unref(source->session);
+	source->session = NULL;
 }
 
 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
@@ -451,10 +346,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
 {
 	struct source *source = user_data;
 	struct pending_request *pending;
-	struct avdtp_local_sep *lsep;
-	struct avdtp_remote_sep *rsep;
-	struct a2dp_sep *sep;
-	GSList *caps = NULL;
 	int id;
 
 	pending = source->connect;
@@ -476,25 +367,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
 
 	DBG("Discovery complete");
 
-	if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
-				A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
-		error("No matching ACP and INT SEPs found");
-		goto failed;
-	}
-
-	if (!select_capabilities(session, rsep, &caps)) {
-		error("Unable to select remote SEP capabilities");
-		goto failed;
-	}
-
-	sep = a2dp_get(session, rsep);
-	if (!sep) {
-		error("Unable to get a local sink SEP");
-		goto failed;
-	}
-
-	id = a2dp_config(source->session, sep, stream_setup_complete, caps,
-				source);
+	id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
+						select_complete, source);
 	if (id == 0)
 		goto failed;
 
diff --git a/audio/transport.c b/audio/transport.c
new file mode 100644
index 0000000..c4d78a4
--- /dev/null
+++ b/audio/transport.c
@@ -0,0 +1,671 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
+
+struct acquire_request {
+	DBusMessage		*msg;
+	guint			id;
+	struct media_renderer	*renderer;
+};
+
+struct media_renderer {
+	struct media_transport	*transport;
+	struct acquire_request *request;
+	char			*name;
+	char			*accesstype;
+	guint			watch;
+};
+
+struct media_transport {
+	DBusConnection		*conn;
+	char			*path;		/* Transport object path */
+	struct audio_device	*device;	/* Transport device */
+	struct avdtp		*session;	/* Signalling session (a2dp only) */
+	struct media_endpoint	*endpoint;	/* Transport endpoint */
+	GSList			*renderers;	/* Transport renderers */
+	uint8_t			*configuration; /* Transport configuration */
+	int			size;		/* Transport configuration size */
+	int			fd;		/* Transport file descriptor */
+	uint16_t		imtu;		/* Transport input mtu */
+	uint16_t		omtu;		/* Transport output mtu */
+	uint16_t		delay;		/* Transport delay (a2dp only) */
+	gboolean		nrec;		/* Transport nrec (hfp only) */
+	gboolean		inband;		/* Transport inband ringtone support (hfp only) */
+	gboolean		read_lock;
+	gboolean		write_lock;
+	gboolean		in_use;
+	guint			(*resume) (struct media_transport *transport,
+					struct media_renderer *renderer);
+	void			(*suspend) (struct media_transport *transport);
+	void			(*cancel) (struct media_transport *transport,
+								guint id);
+	void			(*get_properties) (
+					struct media_transport *transport,
+					DBusMessageIter *dict);
+	DBusMessage		*(*set_property) (
+					struct media_transport *transport,
+					DBusConnection *conn,
+					DBusMessage *msg);
+};
+
+void media_transport_remove(struct media_transport *transport)
+{
+	char *path;
+
+	path = g_strdup(transport->path);
+
+	g_dbus_unregister_interface(transport->conn, path,
+						MEDIA_TRANSPORT_INTERFACE);
+
+	g_free(path);
+}
+
+static void acquire_request_free(struct acquire_request *req)
+{
+	struct media_renderer *renderer = req->renderer;
+	struct media_transport *transport = renderer->transport;
+
+	if (req->id)
+		transport->cancel(renderer->transport, req->id);
+
+	if (req->msg)
+		dbus_message_unref(req->msg);
+
+	renderer->request = NULL;
+	g_free(req);
+}
+
+static gboolean media_transport_release(struct media_transport *transport,
+					const char *accesstype)
+{
+	if (g_strstr_len(accesstype, -1, "r") != NULL) {
+		transport->read_lock = FALSE;
+		DBG("Transport %s: read lock released", transport->path);
+	}
+
+	if (g_strstr_len(accesstype, -1, "w") != NULL) {
+		transport->write_lock = FALSE;
+		DBG("Transport %s: write lock released", transport->path);
+	}
+
+	return TRUE;
+}
+
+static void media_renderer_remove(struct media_renderer *renderer)
+{
+	struct media_transport *transport = renderer->transport;
+
+	media_transport_release(transport, renderer->accesstype);
+
+	if (renderer->watch)
+		g_dbus_remove_watch(transport->conn, renderer->watch);
+
+	if (renderer->request) {
+		DBusMessage *reply = g_dbus_create_error(renderer->request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", strerror(EIO));
+
+		g_dbus_send_message(transport->conn, reply);
+
+		acquire_request_free(renderer->request);
+	}
+
+	transport->renderers = g_slist_remove(transport->renderers, renderer);
+
+	/* Suspend if the is no longer any renderer */
+	if (transport->renderers == NULL)
+		transport->suspend(transport);
+
+	DBG("Renderer removed: sender=%s accesstype=%s", renderer->name,
+							renderer->accesstype);
+
+	g_free(renderer->name);
+	g_free(renderer->accesstype);
+	g_free(renderer);
+}
+
+static gboolean media_transport_set_fd(struct media_transport *transport,
+					int fd, uint16_t imtu, uint16_t omtu)
+{
+	if (transport->fd == fd)
+		return TRUE;
+
+	transport->fd = fd;
+	transport->imtu = imtu;
+	transport->omtu = omtu;
+
+	info("%s: fd(%d) ready", transport->path, fd);
+
+	emit_property_changed(transport->conn, transport->path,
+				MEDIA_TRANSPORT_INTERFACE, "IMTU",
+				DBUS_TYPE_UINT16, &transport->imtu);
+
+	emit_property_changed(transport->conn, transport->path,
+				MEDIA_TRANSPORT_INTERFACE, "OMTU",
+				DBUS_TYPE_UINT16, &transport->omtu);
+
+	return TRUE;
+}
+
+static gboolean remove_renderer(gpointer data)
+{
+	media_renderer_remove(data);
+
+	return FALSE;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+				struct avdtp_error *err, void *user_data)
+{
+	struct media_renderer *renderer = user_data;
+	struct acquire_request *req = renderer->request;
+	struct media_transport *transport = renderer->transport;
+	struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+	struct avdtp_stream *stream;
+	int fd;
+	uint16_t imtu, omtu;
+
+	req->id = 0;
+
+	if (err)
+		goto fail;
+
+	stream = a2dp_sep_get_stream(sep);
+	if (stream == NULL)
+		goto fail;
+
+	if (avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL) ==
+			FALSE)
+		goto fail;
+
+	media_transport_set_fd(transport, fd, imtu, omtu);
+
+	if (g_dbus_send_reply(transport->conn, req->msg,
+				DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID) == FALSE)
+		goto fail;
+
+	return;
+
+fail:
+	/* Let the stream state change before removing the renderer */
+	g_idle_add(remove_renderer, renderer);
+}
+
+static guint resume_a2dp(struct media_transport *transport,
+				struct media_renderer *renderer)
+{
+	struct media_endpoint *endpoint = transport->endpoint;
+	struct audio_device *device = transport->device;
+	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+	if (transport->session == NULL) {
+		transport->session = avdtp_get(&device->src, &device->dst);
+		if (transport->session == NULL)
+			return 0;
+	}
+
+	if (transport->in_use == TRUE)
+		goto done;
+
+	transport->in_use = a2dp_sep_lock(sep, transport->session);
+	if (transport->in_use == FALSE)
+		return 0;
+
+done:
+	return a2dp_resume(transport->session, sep, a2dp_resume_complete,
+				renderer);
+}
+
+static void suspend_a2dp(struct media_transport *transport)
+{
+	struct media_endpoint *endpoint = transport->endpoint;
+	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+	a2dp_sep_unlock(sep, transport->session);
+	transport->in_use = FALSE;
+}
+
+static void cancel_a2dp(struct media_transport *transport, guint id)
+{
+	a2dp_cancel(transport->device, id);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+	struct media_renderer *renderer = user_data;
+	struct acquire_request *req = renderer->request;
+	struct media_transport *transport = renderer->transport;
+	int fd;
+
+	req->id = 0;
+
+	if (dev == NULL)
+		goto fail;
+
+	fd = headset_get_sco_fd(dev);
+	if (fd < 0)
+		goto fail;
+
+	media_transport_set_fd(transport, fd, 48, 48);
+
+	if (g_dbus_send_reply(transport->conn, req->msg,
+				DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID) == FALSE)
+		goto fail;
+
+	return;
+
+fail:
+	media_renderer_remove(renderer);
+}
+
+static guint resume_headset(struct media_transport *transport,
+				struct media_renderer *renderer)
+{
+	struct audio_device *device = transport->device;
+
+	if (transport->in_use == TRUE)
+		goto done;
+
+	transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
+						HEADSET_LOCK_WRITE);
+	if (transport->in_use == FALSE)
+		return 0;
+
+done:
+	return headset_request_stream(device, headset_resume_complete,
+					renderer);
+}
+
+static void suspend_headset(struct media_transport *transport)
+{
+	struct audio_device *device = transport->device;
+
+	headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+	transport->in_use = FALSE;
+}
+
+static void cancel_headset(struct media_transport *transport, guint id)
+{
+	headset_cancel_stream(transport->device, id);
+}
+
+static void media_renderer_exit(DBusConnection *connection, void *user_data)
+{
+	struct media_renderer *renderer = user_data;
+
+	renderer->watch = 0;
+	if (renderer->request != NULL)
+		acquire_request_free(renderer->request);
+
+	media_renderer_remove(renderer);
+}
+
+static gboolean media_transport_acquire(struct media_transport *transport,
+							const char *accesstype)
+{
+	gboolean read_lock = FALSE, write_lock = FALSE;
+
+	if (g_strstr_len(accesstype, -1, "r") != NULL) {
+		if (transport->read_lock == TRUE)
+			return FALSE;
+		read_lock = TRUE;
+	}
+
+	if (g_strstr_len(accesstype, -1, "w") != NULL) {
+		if (transport->write_lock == TRUE)
+			return FALSE;
+		write_lock = TRUE;
+	}
+
+	/* Check invalid accesstype */
+	if (read_lock == FALSE && write_lock == FALSE)
+		return FALSE;
+
+	if (read_lock) {
+		transport->read_lock = read_lock;
+		DBG("Transport %s: read lock acquired", transport->path);
+	}
+
+	if (write_lock) {
+		transport->write_lock = write_lock;
+		DBG("Transport %s: write lock acquired", transport->path);
+	}
+
+
+	return TRUE;
+}
+
+static struct media_renderer *media_renderer_create(
+					struct media_transport *transport,
+					DBusMessage *msg,
+					const char *accesstype)
+{
+	struct media_renderer *renderer;
+
+	renderer = g_new0(struct media_renderer, 1);
+	renderer->transport = transport;
+	renderer->name = g_strdup(dbus_message_get_sender(msg));
+	renderer->accesstype = g_strdup(accesstype);
+	renderer->watch = g_dbus_add_disconnect_watch(transport->conn,
+							renderer->name,
+							media_renderer_exit,
+							renderer, NULL);
+	transport->renderers = g_slist_append(transport->renderers, renderer);
+
+	DBG("Renderer created: sender=%s accesstype=%s", renderer->name,
+			accesstype);
+
+	return renderer;
+}
+
+static struct media_renderer *media_transport_find_renderer(
+					struct media_transport *transport,
+					const char *name)
+{
+	GSList *l;
+
+	for (l = transport->renderers; l; l = l->next) {
+		struct media_renderer *renderer = l->data;
+
+		if (g_strcmp0(renderer->name, name) == 0)
+			return renderer;
+	}
+
+	return NULL;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	struct media_renderer *renderer;
+	struct acquire_request *req;
+	const char *accesstype, *sender;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_STRING, &accesstype,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	sender = dbus_message_get_sender(msg);
+
+	renderer = media_transport_find_renderer(transport, sender);
+	if (renderer != NULL)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+
+	if (media_transport_acquire(transport, accesstype) == FALSE)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+
+	renderer = media_renderer_create(transport, msg, accesstype);
+	req = g_new0(struct acquire_request, 1);
+	req->msg = dbus_message_ref(msg);
+	req->renderer = renderer;
+	req->id = transport->resume(transport, renderer);
+	renderer->request = req;
+	if (req->id == 0)
+		media_renderer_remove(renderer);
+
+	return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	struct media_renderer *renderer;
+	const char *accesstype, *sender;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_STRING, &accesstype,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	sender = dbus_message_get_sender(msg);
+
+	renderer = media_transport_find_renderer(transport, sender);
+	if (renderer == NULL)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+
+	if (g_strcmp0(renderer->accesstype, accesstype) == 0)
+		media_renderer_remove(renderer);
+	else if (g_strstr_len(renderer->accesstype, -1, accesstype) != NULL) {
+		media_transport_release(transport, accesstype);
+		g_strdelimit(renderer->accesstype, accesstype, ' ');
+	} else
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *set_property_a2dp(struct media_transport *transport,
+					DBusConnection *conn, DBusMessage *msg)
+{
+	return NULL;
+}
+
+static DBusMessage *set_property_headset(struct media_transport *transport,
+					DBusConnection *conn, DBusMessage *msg)
+{
+	return NULL;
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_transport *transport = data;
+
+	return transport->set_property(transport, conn, msg);
+}
+
+static void get_properties_a2dp(struct media_transport *transport,
+						DBusMessageIter *dict)
+{
+	dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
+}
+
+static void get_properties_headset(struct media_transport *transport,
+						DBusMessageIter *dict)
+{
+	dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &transport->nrec);
+
+	dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN,
+							&transport->inband);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *uuid;
+	uint8_t codec;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	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_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	/* Device */
+	dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
+						&transport->device->path);
+
+	dict_append_entry(&dict, "ReadLock", DBUS_TYPE_BOOLEAN,
+						&transport->read_lock);
+
+	dict_append_entry(&dict, "WriteLock", DBUS_TYPE_BOOLEAN,
+						&transport->write_lock);
+
+	dict_append_entry(&dict, "IMTU", DBUS_TYPE_UINT16,
+						&transport->imtu);
+
+	dict_append_entry(&dict, "OMTU", DBUS_TYPE_UINT16,
+						&transport->omtu);
+
+	uuid = media_endpoint_get_uuid(transport->endpoint);
+	dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+	codec = media_endpoint_get_codec(transport->endpoint);
+	dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
+
+	dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
+				&transport->configuration, transport->size);
+
+	if (transport->get_properties)
+		transport->get_properties(transport, &dict);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static GDBusMethodTable transport_methods[] = {
+	{ "GetProperties",	"",	"a{sv}",	get_properties },
+	{ "Acquire",		"s",	"h",		acquire,
+						G_DBUS_METHOD_FLAG_ASYNC},
+	{ "Release",		"s",	"",		release },
+	{ "SetProperty",	"sv",	"",		set_property },
+	{ },
+};
+
+static GDBusSignalTable transport_signals[] = {
+	{ "PropertyChanged",	"sv"	},
+	{ }
+};
+
+static void media_transport_free(void *data)
+{
+	struct media_transport *transport = data;
+
+	g_slist_foreach(transport->renderers, (GFunc) media_renderer_remove,
+				NULL);
+	g_slist_free(transport->renderers);
+
+	if (transport->session)
+		avdtp_unref(transport->session);
+
+	if (transport->conn)
+		dbus_connection_unref(transport->conn);
+
+	g_free(transport->configuration);
+	g_free(transport->path);
+	g_free(transport);
+}
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+						struct media_endpoint *endpoint,
+						struct audio_device *device,
+						uint8_t *configuration,
+						size_t size)
+{
+	struct media_transport *transport;
+	const char *uuid;
+	static int fd = 0;
+
+	transport = g_new0(struct media_transport, 1);
+	transport->conn = dbus_connection_ref(conn);
+	transport->device = device;
+	transport->endpoint = endpoint;
+	transport->configuration = g_new(uint8_t, size);
+	memcpy(transport->configuration, configuration, size);
+	transport->size = size;
+	transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
+	transport->fd = -1;
+
+	uuid = media_endpoint_get_uuid(endpoint);
+	if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0 ||
+			g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+		transport->resume = resume_a2dp;
+		transport->suspend = suspend_a2dp;
+		transport->cancel = cancel_a2dp;
+		transport->get_properties = get_properties_a2dp;
+		transport->set_property = set_property_a2dp;
+	} else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
+			g_strcmp0(uuid, HSP_AG_UUID) == 0) {
+		transport->resume = resume_headset;
+		transport->suspend = suspend_headset;
+		transport->cancel = cancel_headset;
+		transport->get_properties = get_properties_headset;
+		transport->set_property = set_property_headset;
+	} else
+		goto fail;
+
+	if (g_dbus_register_interface(transport->conn, transport->path,
+				MEDIA_TRANSPORT_INTERFACE,
+				transport_methods, transport_signals, NULL,
+				transport, media_transport_free) == FALSE) {
+		error("Could not register transport %s", transport->path);
+		goto fail;
+	}
+
+	return transport;
+
+fail:
+	media_transport_free(transport);
+	return NULL;
+}
+
+const char *media_transport_get_path(struct media_transport *transport)
+{
+	return transport->path;
+}
diff --git a/audio/transport.h b/audio/transport.h
new file mode 100644
index 0000000..a7594f0
--- /dev/null
+++ b/audio/transport.h
@@ -0,0 +1,34 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct media_transport;
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+						struct media_endpoint *endpoint,
+						struct audio_device *device,
+						uint8_t *configuration,
+						size_t size);
+
+void media_transport_remove(struct media_transport *transport);
+const char *media_transport_get_path(struct media_transport *transport);
diff --git a/audio/unix.c b/audio/unix.c
index cf2704c..ad822bd 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -44,6 +44,7 @@
 #include "device.h"
 #include "manager.h"
 #include "avdtp.h"
+#include "media.h"
 #include "a2dp.h"
 #include "headset.h"
 #include "sink.h"
-- 
1.7.1


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

* [PATCH 07/10] Introduce headset_get_inband
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 06/10] Add initial implementation of org.bluez.Media spec Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 08/10] Update a2dp transport delay when it changes Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 audio/headset.c |   10 ++++++++++
 audio/headset.h |    1 +
 2 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/audio/headset.c b/audio/headset.c
index cdb3370..dad0716 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -2701,6 +2701,16 @@ gboolean headset_get_nrec(struct audio_device *dev)
 	return hs->slc->nrec;
 }
 
+gboolean headset_get_inband(struct audio_device *dev)
+{
+	struct headset *hs = dev->headset;
+
+	if (!hs->slc)
+		return TRUE;
+
+	return hs->slc->inband_ring;
+}
+
 gboolean headset_get_sco_hci(struct audio_device *dev)
 {
 	return sco_hci;
diff --git a/audio/headset.h b/audio/headset.h
index 8b7c738..29dc02c 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -90,6 +90,7 @@ int headset_get_channel(struct audio_device *dev);
 
 int headset_get_sco_fd(struct audio_device *dev);
 gboolean headset_get_nrec(struct audio_device *dev);
+gboolean headset_get_inband(struct audio_device *dev);
 gboolean headset_get_sco_hci(struct audio_device *dev);
 
 gboolean headset_is_active(struct audio_device *dev);
-- 
1.7.1


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

* [PATCH 08/10] Update a2dp transport delay when it changes
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 07/10] Introduce headset_get_inband Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 09/10] Remove local cache for nrec and inband Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 audio/a2dp.c      |   31 +++++++++++++++++++++++++++----
 audio/media.c     |    6 ++++++
 audio/media.h     |    2 ++
 audio/transport.c |   14 ++++++++++++++
 audio/transport.h |    2 ++
 5 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index e6daf3b..7d3f25f 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -44,6 +44,7 @@
 #include "source.h"
 #include "unix.h"
 #include "media.h"
+#include "transport.h"
 #include "a2dp.h"
 #include "sdpd.h"
 
@@ -600,9 +601,9 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
 }
 
 static gboolean endpoint_getcap_ind(struct avdtp *session,
-				struct avdtp_local_sep *sep,
-				gboolean get_all,
-				GSList **caps, uint8_t *err, void *user_data)
+					struct avdtp_local_sep *sep,
+					gboolean get_all, GSList **caps,
+					uint8_t *err, void *user_data)
 {
 	struct a2dp_sep *a2dp_sep = user_data;
 	struct avdtp_service_capability *media_transport, *media_codec;
@@ -1031,6 +1032,28 @@ static gboolean delayreport_ind(struct avdtp *session,
 	return TRUE;
 }
 
+static gboolean endpoint_delayreport_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						uint8_t rseid, uint16_t delay,
+						uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct media_transport *transport;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: DelayReport_Ind", sep);
+	else
+		DBG("Source %p: DelayReport_Ind", sep);
+
+	transport = media_endpoint_get_transport(a2dp_sep->endpoint);
+	if (transport == NULL)
+		return FALSE;
+
+	media_transport_update_delay(transport, delay);
+
+	return TRUE;
+}
+
 static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			struct avdtp_stream *stream, struct avdtp_error *err,
 			void *user_data)
@@ -1115,7 +1138,7 @@ static struct avdtp_sep_ind endpoint_ind = {
 	.close			= close_ind,
 	.abort			= abort_ind,
 	.reconfigure		= reconf_ind,
-	.delayreport		= delayreport_ind,
+	.delayreport		= endpoint_delayreport_ind,
 };
 
 static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
diff --git a/audio/media.c b/audio/media.c
index da9d3ae..19ba1c4 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -681,3 +681,9 @@ uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
 {
 	return endpoint->codec;
 }
+
+struct media_transport *media_endpoint_get_transport(
+					struct media_endpoint *endpoint)
+{
+	return endpoint->transport;
+}
diff --git a/audio/media.h b/audio/media.h
index f6d144c..d089103 100644
--- a/audio/media.h
+++ b/audio/media.h
@@ -50,3 +50,5 @@ void media_endpoint_release(struct media_endpoint *endpoint);
 struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
 const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
 uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct media_transport *media_endpoint_get_transport(
+					struct media_endpoint *endpoint);
diff --git a/audio/transport.c b/audio/transport.c
index c4d78a4..0da99e8 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -669,3 +669,17 @@ const char *media_transport_get_path(struct media_transport *transport)
 {
 	return transport->path;
 }
+
+void media_transport_update_delay(struct media_transport *transport,
+							uint16_t delay)
+{
+	/* Check if delay really changed */
+	if (transport->delay == delay)
+		return;
+
+	transport->delay = delay;
+
+	emit_property_changed(transport->conn, transport->path,
+				MEDIA_TRANSPORT_INTERFACE, "Delay",
+				DBUS_TYPE_UINT16, &transport->delay);
+}
diff --git a/audio/transport.h b/audio/transport.h
index a7594f0..f2eb37b 100644
--- a/audio/transport.h
+++ b/audio/transport.h
@@ -32,3 +32,5 @@ struct media_transport *media_transport_create(DBusConnection *conn,
 
 void media_transport_remove(struct media_transport *transport);
 const char *media_transport_get_path(struct media_transport *transport);
+void media_transport_update_delay(struct media_transport *transport,
+							uint16_t delay);
-- 
1.7.1


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

* [PATCH 09/10] Remove local cache for nrec and inband
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 08/10] Update a2dp transport delay when it changes Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-13 14:15 ` [PATCH 10/10] Add proper checks for MediaTransport.SetProperty Luiz Augusto von Dentz
  2010-09-15 13:35 ` [PATCH 00/10] Media API Johan Hedberg
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

Those can be acessed directly from device/headset.
---
 audio/transport.c |   11 ++++++-----
 1 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/audio/transport.c b/audio/transport.c
index 0da99e8..6d08677 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -72,8 +72,6 @@ struct media_transport {
 	uint16_t		imtu;		/* Transport input mtu */
 	uint16_t		omtu;		/* Transport output mtu */
 	uint16_t		delay;		/* Transport delay (a2dp only) */
-	gboolean		nrec;		/* Transport nrec (hfp only) */
-	gboolean		inband;		/* Transport inband ringtone support (hfp only) */
 	gboolean		read_lock;
 	gboolean		write_lock;
 	gboolean		in_use;
@@ -519,10 +517,13 @@ static void get_properties_a2dp(struct media_transport *transport,
 static void get_properties_headset(struct media_transport *transport,
 						DBusMessageIter *dict)
 {
-	dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &transport->nrec);
+	gboolean nrec, inband;
 
-	dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN,
-							&transport->inband);
+	nrec = headset_get_nrec(transport->device);
+	dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
+
+	inband = headset_get_inband(transport->device);
+	dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
 }
 
 static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
-- 
1.7.1


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

* [PATCH 10/10] Add proper checks for MediaTransport.SetProperty
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (8 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 09/10] Remove local cache for nrec and inband Luiz Augusto von Dentz
@ 2010-09-13 14:15 ` Luiz Augusto von Dentz
  2010-09-15 13:35 ` [PATCH 00/10] Media API Johan Hedberg
  10 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 audio/transport.c |  116 +++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 94 insertions(+), 22 deletions(-)

diff --git a/audio/transport.c b/audio/transport.c
index 6d08677..7387818 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -83,12 +83,23 @@ struct media_transport {
 	void			(*get_properties) (
 					struct media_transport *transport,
 					DBusMessageIter *dict);
-	DBusMessage		*(*set_property) (
+	int			(*set_property) (
 					struct media_transport *transport,
-					DBusConnection *conn,
-					DBusMessage *msg);
+					const char *property,
+					DBusMessageIter *value);
 };
 
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+}
+
+static inline DBusMessage *error_failed(DBusMessage *msg, const char *desc)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", desc);
+}
+
 void media_transport_remove(struct media_transport *transport)
 {
 	char *path;
@@ -434,14 +445,10 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 
 	renderer = media_transport_find_renderer(transport, sender);
 	if (renderer != NULL)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Permission denied");
+		return error_failed(msg, strerror(EPERM));
 
 	if (media_transport_acquire(transport, accesstype) == FALSE)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Permission denied");
+		return error_failed(msg, strerror(EPERM));
 
 	renderer = media_renderer_create(transport, msg, accesstype);
 	req = g_new0(struct acquire_request, 1);
@@ -471,9 +478,7 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 
 	renderer = media_transport_find_renderer(transport, sender);
 	if (renderer == NULL)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Permission denied");
+		return error_failed(msg, strerror(EPERM));
 
 	if (g_strcmp0(renderer->accesstype, accesstype) == 0)
 		media_renderer_remove(renderer);
@@ -481,31 +486,98 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 		media_transport_release(transport, accesstype);
 		g_strdelimit(renderer->accesstype, accesstype, ' ');
 	} else
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Permission denied");
+		return error_failed(msg, strerror(EPERM));
 
 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static DBusMessage *set_property_a2dp(struct media_transport *transport,
-					DBusConnection *conn, DBusMessage *msg)
+static int set_property_a2dp(struct media_transport *transport,
+						const char *property,
+						DBusMessageIter *value)
 {
-	return NULL;
+	if (g_strcmp0(property, "Delay") == 0) {
+		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &transport->delay);
+
+		/* FIXME: send new delay */
+		return 0;
+	}
+
+	return -EINVAL;
 }
 
-static DBusMessage *set_property_headset(struct media_transport *transport,
-					DBusConnection *conn, DBusMessage *msg)
+static int set_property_headset(struct media_transport *transport,
+						const char *property,
+						DBusMessageIter *value)
 {
-	return NULL;
+	if (g_strcmp0(property, "NREC") == 0) {
+		gboolean nrec;
+
+		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &nrec);
+
+		/* FIXME: set new nrec */
+		return 0;
+	} else if (g_strcmp0(property, "InbandRingtone") == 0) {
+		gboolean inband;
+
+		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &inband);
+
+		/* FIXME: set new inband */
+		return 0;
+	}
+
+	return -EINVAL;
 }
 
 static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
 	struct media_transport *transport = data;
+	DBusMessageIter iter;
+	DBusMessageIter value;
+	const char *property, *sender;
+	GSList *l;
+	int err;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &property);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return invalid_args(msg);
+	dbus_message_iter_recurse(&iter, &value);
+
+	sender = dbus_message_get_sender(msg);
+	err = -EINVAL;
 
-	return transport->set_property(transport, conn, msg);
+	/* Check if sender has acquired the transport */
+	for (l = transport->renderers; l; l = l->next) {
+		struct media_renderer *renderer = l->data;
+
+		if (g_strcmp0(renderer->name, sender) == 0) {
+			err = transport->set_property(transport, property,
+								&value);
+			break;
+		}
+	}
+
+	if (err < 0) {
+		if (err == -EINVAL)
+			return invalid_args(msg);
+		return error_failed(msg, strerror(-err));
+	}
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
 static void get_properties_a2dp(struct media_transport *transport,
-- 
1.7.1


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

* Re: [PATCH 00/10] Media API
  2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
                   ` (9 preceding siblings ...)
  2010-09-13 14:15 ` [PATCH 10/10] Add proper checks for MediaTransport.SetProperty Luiz Augusto von Dentz
@ 2010-09-15 13:35 ` Johan Hedberg
  10 siblings, 0 replies; 12+ messages in thread
From: Johan Hedberg @ 2010-09-15 13:35 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

On Mon, Sep 13, 2010, Luiz Augusto von Dentz wrote:
> Luiz Augusto von Dentz (10):
>   Add media API documentation
>   Add rule to enabling talking to org.bluez.MediaEndpoint
>   Add option to enable/disable unix ipc via audio.conf
>   Add support for media transport in gstreamer plugin
>   Add simple-endpoint test script
>   Add initial implementation of org.bluez.Media spec
>   Introduce headset_get_inband
>   Update a2dp transport delay when it changes
>   Remove local cache for nrec and inband
>   Add proper checks for MediaTransport.SetProperty
> 
>  Makefile.am          |    6 +-
>  audio/a2dp-codecs.h  |  116 ++++++++
>  audio/a2dp.c         |  749 +++++++++++++++++++++++++++++++++++++++++--------
>  audio/a2dp.h         |   15 +
>  audio/avdtp.c        |   88 +++----
>  audio/avdtp.h        |    5 +-
>  audio/gsta2dpsink.c  |   33 ++-
>  audio/gsta2dpsink.h  |    1 +
>  audio/gstavdtpsink.c |  609 +++++++++++++++++++++++++++++++++++++++-
>  audio/gstavdtpsink.h |    6 +
>  audio/headset.c      |   10 +
>  audio/headset.h      |    1 +
>  audio/main.c         |    9 -
>  audio/manager.c      |   66 +++++
>  audio/manager.h      |    2 +
>  audio/media.c        |  689 +++++++++++++++++++++++++++++++++++++++++++++
>  audio/media.h        |   54 ++++
>  audio/sink.c         |  179 ++-----------
>  audio/source.c       |  174 ++----------
>  audio/transport.c    |  758 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  audio/transport.h    |   36 +++
>  audio/unix.c         |    1 +
>  doc/media-api.txt    |  169 +++++++++++
>  src/bluetooth.conf   |    1 +
>  test/simple-endpoint |  126 +++++++++
>  25 files changed, 3395 insertions(+), 508 deletions(-)
>  create mode 100644 audio/a2dp-codecs.h
>  create mode 100644 audio/media.c
>  create mode 100644 audio/media.h
>  create mode 100644 audio/transport.c
>  create mode 100644 audio/transport.h
>  create mode 100644 doc/media-api.txt
>  create mode 100755 test/simple-endpoint

All patches have been pushed upstream now (with the few minor changes
that we discussed). Next step: talk to PulseAudio guys and get this
merged there too.

Johan

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

end of thread, other threads:[~2010-09-15 13:35 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-13 14:15 [PATCH 00/10] Media API Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 01/10] Add media API documentation Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 02/10] Add rule to enabling talking to org.bluez.MediaEndpoint Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 03/10] Add option to enable/disable unix ipc via audio.conf Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 04/10] Add support for media transport in gstreamer plugin Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 05/10] Add simple-endpoint test script Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 06/10] Add initial implementation of org.bluez.Media spec Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 07/10] Introduce headset_get_inband Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 08/10] Update a2dp transport delay when it changes Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 09/10] Remove local cache for nrec and inband Luiz Augusto von Dentz
2010-09-13 14:15 ` [PATCH 10/10] Add proper checks for MediaTransport.SetProperty Luiz Augusto von Dentz
2010-09-15 13:35 ` [PATCH 00/10] Media API Johan Hedberg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.