Re: [RFC] Media API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Tue, Jan 5, 2010 at 1:39 AM, Marcel Holtmann <marcel@xxxxxxxxxxxx> wrote:
> Hi Luiz,
>
>> The idea is to complete replace the existing audio IPC with DBus.
>> Johan and I discussed this a few times in the past and today we
>> finally archive something, so here are some design choices so far:
>>
>> 1. Codec capabilities and configuration are blobs (array of bytes or
>> 'ay'), so there is no attempt to format codec structures into dbus
>> structures, this make it easier for both end points as well as
>> bluetoothd and also enables proprietary codecs. (suggested by Marcel
>> in the last BlueZ meeting)
>>
>> 2. The spec is not a2dp specific. So it should be possible to register
>> end points for HFP and VDP.
>
> if you really wanna achieve that, then I would prefer not to use
> StreamEndPoint as interface.
>
> So first of all it should be StreamEndpoint. While SEP takes the P into
> account, the word "endpoint" in itself is proper enough.
>
> And if this should support HFP, then the term Stream is kinda tricky. We
> could just allow that, but I prefer not to mix streaming with headset
> functionality. So what about just using the "Endpoint" as a generic
> agent type interface. Or "MediaEndpoint" if you need to tie it to the
> media interface?

Here are the final documentation and a very basic implementation, note
that to compile it needs dbus 1.3 (@Marcel: What about making it a
hard dependency?), other remarks:

- Media interface is disabled by default, to enable it one has to edit
audio.conf e.g. Enable=Media
- simple-endpoint can be used to emulate an endpoint e.g
./simple-endpoint hci0 sbcsource (other predefined are: sbcsink
mp3source mp3sink)
- Due to the timeout of avdtp requests (4 sec.) Ive been using 3 sec
for endpoint method calls, that why simple-endpoint always respond a
predefined configuration when SelectConfiguration although
SetConfiguration still requires an answer.
- To not have an even bigger change which would reflect on unix socket
support I had to make media_endpoint_set_configuration to block when
not given a callback, this is on purpose because we are replying to
avdtp commands in a sync manner.
- There are still a lot missing such as: making proper use of caller
of {Audio,Headset,AudioSource,AudioSink}.Connect when selecting an
endpoint, hfp support, simple-endpoint for hfp roles, proper transport
locking, file descriptor passing.


-- 
Luiz Augusto von Dentz
Engenheiro de Computação
From 4759a880a30691a7ae9081154e2e06ac7eaf94be Mon Sep 17 00:00:00 2001
From: Luiz Augusto Von Dentz <luiz.dentz-von@xxxxxxxxx>
Date: Wed, 13 Jan 2010 16:56:23 +0200
Subject: [PATCH 1/4] Add media API documentation

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 |  123 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 123 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..b7c641c
--- /dev/null
+++ b/doc/media-api.txt
@@ -0,0 +1,123 @@
+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.
+
+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 are changeable.
+			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	uint16 Delay [readwrite]
+
+			Optional. Transport delay in 1/10 of milisecond, this
+			property is only writeable when the transport was
+			acquired by the sender and the sender is a sink.
+
+		boolean NREC [readwrite]
+
+			Optional. It tells 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.
+
+		object Device [readonly]
+
+			Device object which the transport is connected to.
-- 
1.6.3.3

From 3577ee604187c758180bcea6ecd404fc8f4c357e Mon Sep 17 00:00:00 2001
From: Luiz Augusto Von Dentz <luiz.dentz-von@xxxxxxxxx>
Date: Wed, 20 Jan 2010 14:53:00 +0200
Subject: [PATCH 2/4] Add simple-endpoint test script

---
 test/simple-endpoint |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 106 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..5fee8bf
--- /dev/null
+++ b/test/simple-endpoint
@@ -0,0 +1,106 @@
+#!/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"
+
+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: 64-64
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x12), dbus.Byte(0x15), dbus.Byte(64), dbus.Byte(64)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 22.05Khz 24Khz 32Khz 44.1Khz 48Khz
+#CRC: No
+#Layer: 1 2 3
+#Bit Rate: Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0xef), dbus.Byte(0x3f), dbus.Byte(0xff), dbus.Byte(0xff)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x22), dbus.Byte(0x02), dbus.Byte(0x80), dbus.Byte(0x00)])
+
+class Rejected(dbus.DBusException):
+	_dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+	configuration = SBC_CONFIGURATION
+
+	def default_configuration(self, configuration):
+		self.configuration = configuration
+
+	@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)
+		accept = raw_input("Accept configuration (yes/no): ")
+		if (accept == "yes"):
+			return
+		raise Rejected("Invalid configuration")
+
+	@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,
+					"Capabilities" : SBC_CAPABILITIES })
+
+	if len(sys.argv) > 2:
+		if sys.argv[2] == "sbcsink":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+							"Codec" : SBC_CODEC,
+							"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_SOURCE_UUID,
+							"Codec" : MP3_CODEC,
+							"Capabilities" : MP3_CAPABILITIES })
+			endpoint.default_configuration(MP3_CONFIGURATION)
+
+	print properties
+
+	media.RegisterEndpoint(path, properties)
+
+	mainloop.run()
-- 
1.6.3.3

From e372552bd220b4edac601c278e75d5c2f54fe5dc Mon Sep 17 00:00:00 2001
From: Luiz Augusto Von Dentz <luiz.dentz-von@xxxxxxxxx>
Date: Thu, 21 Jan 2010 18:23:45 +0200
Subject: [PATCH 3/4] Add rule to enabling talking to org.bluez.MediaEndpoint

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

diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 315009c..6bc6bba 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -11,6 +11,7 @@
     <allow own="org.bluez"/>
     <allow send_destination="org.bluez"/>
     <allow send_interface="org.bluez.Agent"/>
+    <allow send_interface="org.bluez.MediaEndpoint"/>
   </policy>
 
   <policy at_console="true">
-- 
1.6.3.3

From 3f2adc271f9da153518b301d3da6e47d178818e0 Mon Sep 17 00:00:00 2001
From: Luiz Augusto Von Dentz <luiz.dentz-von@xxxxxxxxx>
Date: Thu, 28 Jan 2010 15:11:13 +0200
Subject: [PATCH 4/4] Add initial implementation of org.bluez.Media spec

---
 Makefile.am     |    1 +
 audio/a2dp.c    |  592 +++++++++++++++++++++++++++++++++++++-----
 audio/a2dp.h    |   14 +
 audio/avdtp.c   |    3 +
 audio/manager.c |   50 ++++
 audio/manager.h |    1 +
 audio/media.c   |  778 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 audio/media.h   |   45 ++++
 audio/sink.c    |  178 ++-----------
 audio/source.c  |  174 ++-----------
 audio/unix.c    |    1 +
 11 files changed, 1463 insertions(+), 374 deletions(-)
 create mode 100644 audio/media.c
 create mode 100644 audio/media.h

diff --git a/Makefile.am b/Makefile.am
index f8111a9..c55834a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -121,6 +121,7 @@ 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/telephony.h
 builtin_nodist += audio/telephony.c
 
diff --git a/audio/a2dp.c b/audio/a2dp.c
index 575291a..05d5971 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,6 +61,8 @@
 #endif
 
 struct a2dp_sep {
+	struct a2dp_server *server;
+	struct media_endpoint *endpoint;
 	uint8_t type;
 	uint8_t codec;
 	struct avdtp_local_sep *sep;
@@ -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;
@@ -87,6 +91,7 @@ struct a2dp_setup {
 	struct avdtp_stream *stream;
 	struct avdtp_error *err;
 	GSList *client_caps;
+	gboolean delay_reporting;
 	gboolean reconfigure;
 	gboolean canceled;
 	gboolean start;
@@ -228,6 +233,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;
@@ -277,6 +301,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;
 
 }
@@ -506,6 +533,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)
+		debug("Sink %p: Set_Configuration_Ind", sep);
+	else
+		debug("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)
+		debug("Sink %p: Get_Capability_Ind", sep);
+	else
+		debug("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_malloc(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)
@@ -544,6 +682,22 @@ 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 = avdtp_stream_get_codec(stream);
+		struct avdtp_media_codec_capability *codec;
+
+		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);
@@ -1005,6 +1159,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;
@@ -1072,64 +1239,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;
@@ -1250,22 +1359,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;
@@ -1309,6 +1419,91 @@ 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->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->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->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;
+}
+
+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);
+	else
+		server->sinks = g_slist_remove(server->sinks, sep);
+
+	a2dp_unregister_sep(sep);
+}
+
 struct a2dp_sep *a2dp_get(struct avdtp *session,
 				struct avdtp_remote_sep *rsep)
 {
@@ -1347,6 +1542,269 @@ 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, size_t 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) {
+		debug("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);
+
+	if (setup->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);
+	}
+
+done:
+	finalize_select(setup, caps);
+}
+
+static gboolean auto_select(gpointer data)
+{
+	struct a2dp_setup *setup = data;
+
+	finalize_select(setup, setup->client_caps);
+
+	return FALSE;
+}
+
+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_local_sep *lsep;
+	struct avdtp_remote_sep *rsep;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+
+	if (avdtp_get_seps(session, type, AVDTP_MEDIA_TYPE_AUDIO,
+				A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
+		error("No matching ACP and INT SEPs found");
+		return 0;
+	}
+
+	sep = a2dp_get(session, rsep);
+	if (!sep) {
+		error("Unable to get a local source 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->delay_reporting = (sep->delay_reporting &&
+//				avdtp_get_delay_reporting(rsep));
+
+	/* 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, 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(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)
@@ -1620,7 +2078,7 @@ gboolean a2dp_cancel(struct audio_device *dev, unsigned int id)
 
 gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
 {
-	if (sep->locked)
+	if (sep->locked || sep->endpoint)
 		return FALSE;
 
 	debug("SEP %p locked", sep->sep);
diff --git a/audio/a2dp.h b/audio/a2dp.h
index fa81776..16bac53 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);
diff --git a/audio/avdtp.c b/audio/avdtp.c
index e5622e4..a844cab 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -3544,6 +3544,9 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep)
 	if (sep->stream)
 		release_stream(sep->stream, sep->stream->session);
 
+	debug("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/manager.c b/audio/manager.c
index 413c1f3..2b16b6c 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"
@@ -113,6 +114,7 @@ static struct enabled_interfaces enabled = {
 	.sink		= TRUE,
 	.source		= FALSE,
 	.control	= TRUE,
+	.media		= FALSE
 };
 
 static struct audio_adapter *find_adapter(GSList *list,
@@ -1022,6 +1024,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,
@@ -1055,6 +1089,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)
 {
@@ -1083,6 +1123,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], "Media"))
+			enabled.media = TRUE;
 	}
 	g_strfreev(list);
 
@@ -1099,6 +1141,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], "Media"))
+			enabled.media = FALSE;
 	}
 	g_strfreev(list);
 
@@ -1140,6 +1184,9 @@ proceed:
 	if (enabled.control)
 		btd_register_adapter_driver(&avrcp_server_driver);
 
+	if (enabled.media)
+		btd_register_adapter_driver(&media_server_driver);
+
 	btd_register_device_driver(&audio_driver);
 
 	*enable_sco = (enabled.gateway || enabled.headset);
@@ -1175,6 +1222,9 @@ void audio_manager_exit(void)
 	if (enabled.control)
 		btd_unregister_adapter_driver(&avrcp_server_driver);
 
+	if (enabled.media)
+		btd_unregister_adapter_driver(&media_server_driver);
+
 	btd_unregister_device_driver(&audio_driver);
 }
 
diff --git a/audio/manager.h b/audio/manager.h
index 8e1abf4..bee2a9b 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -29,6 +29,7 @@ struct enabled_interfaces {
 	gboolean sink;
 	gboolean source;
 	gboolean control;
+	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..f24d354
--- /dev/null
+++ b/audio/media.c
@@ -0,0 +1,778 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@xxxxxxxxxxxx>
+ *
+ *
+ *  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 "logging.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+
+#define MEDIA_INTERFACE "org.bluez.Media"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
+
+#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			watch;
+	struct endpoint_request *request;
+	struct media_transport	*transport;
+	struct media_adapter	*adapter;
+};
+
+struct media_transport {
+	char			*path;		/* Transport object path */
+	struct audio_device	*device;	/* Transport device */
+	int			fd;		/* Transport file descriptor */
+	uint16_t		delay;		/* Transport delay (a2dp only) */
+	gboolean		nrec;		/* Transport nrec (hfp only) */
+	gboolean		read_lock;
+	gboolean		write_lock;
+};
+
+static GSList *adapters = NULL;
+
+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->transport) {
+		g_free(endpoint->transport->path);
+		g_free(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 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,
+						size_t 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);
+	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) {
+		g_free(endpoint);
+		return NULL;
+	}
+
+	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;
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+						struct media_adapter *adapter,
+						const char *sender,
+						const char *path)
+{
+	GSList *l;
+
+	for (l = adapter->endpoints; l; l = l->next) {
+		struct media_endpoint *endpoint = l->data;
+
+		if (g_strcmp0(endpoint->sender, sender) != 0)
+			continue;
+
+		if (g_strcmp0(endpoint->path, path) == 0)
+			return endpoint;
+	}
+
+	return NULL;
+}
+
+static int parse_byte_array(DBusMessageIter *iter, uint8_t *value, size_t *length)
+{
+	DBusMessageIter array;
+	size_t i;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	for (i = 0; dbus_message_iter_get_arg_type(&array) !=
+					DBUS_TYPE_INVALID; i++) {
+
+		if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+			return -EINVAL;
+
+		if (i >= *length)
+			return -ENOMEM;
+
+		dbus_message_iter_get_basic(&array, &value[i]);
+
+		dbus_message_iter_next(&array);
+	}
+
+	*length = i;
+
+	return 0;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+				gboolean *delay_reporting, uint8_t *codec,
+				uint8_t *capabilities, size_t *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) {
+			int ret = parse_byte_array(&value, capabilities, size);
+
+			if (ret < 0)
+				return ret;
+		}
+
+		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[128];
+	size_t size = sizeof(capabilities);
+
+	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)
+		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");
+
+	media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+				codec, capabilities, size);
+
+	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);
+	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;
+
+	dbus_connection_unref(adapter->conn);
+
+	g_free(adapter->path);
+	g_free(adapter);
+}
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
+{
+	struct media_adapter *adapter;
+
+	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;
+	}
+
+	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;
+		}
+	}
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+
+	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);
+
+	/* Delay (a2dp only) */
+	if (transport->delay > 0)
+		dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT16,
+					&transport->delay);
+
+	/* NREC (hfp only) */
+	if (transport->nrec)
+		dict_append_entry(&dict, "NREC", DBUS_TYPE_BOOLEAN,
+					&transport->nrec);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	const char *accesstype;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_STRING, &accesstype,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	if (g_strstr_len(accesstype, -1, "r") != NULL) {
+		if (transport->read_lock == TRUE)
+			return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+		transport->read_lock = TRUE;
+	}
+
+	if (g_strstr_len(accesstype, -1, "w") != NULL) {
+		if (transport->write_lock == TRUE)
+			return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+		transport->write_lock = TRUE;
+	}
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, transport->fd,
+				DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	const char *accesstype;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_STRING, &accesstype,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	if (g_strstr_len(accesstype, -1, "r") != NULL) {
+		if (transport->read_lock == FALSE)
+			return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+		transport->read_lock = FALSE;
+	}
+
+	if (g_strstr_len(accesstype, -1, "w") != NULL) {
+		if (transport->write_lock == FALSE)
+			return g_dbus_create_error(msg, ERROR_INTERFACE
+						".Failed",
+						"Permission denied");
+		transport->write_lock = FALSE;
+	}
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable transport_methods[] = {
+	{ "GetProperties",	"",	"a{sv}",	get_properties },
+	{ "Acquire",		"s",	"u",		acquire },
+	{ "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_free(transport);
+}
+
+static void media_transport_remove(DBusConnection *conn,
+					struct media_transport *transport)
+{
+	g_dbus_unregister_interface(conn, transport->path,
+			MEDIA_TRANSPORT_INTERFACE);
+}
+
+static struct media_transport *media_transport_create(DBusConnection *conn,
+						struct audio_device *device)
+{
+	struct media_transport *transport;
+
+	static int fd = 0;
+
+	transport = g_new0(struct media_transport, 1);
+	transport->device = device;
+	transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
+	transport->fd = -1;
+
+	if (g_dbus_register_interface(conn, transport->path,
+				MEDIA_TRANSPORT_INTERFACE,
+				transport_methods, transport_signals, NULL,
+				transport, media_transport_free) == FALSE) {
+		error("Could not register transport %s", transport->path);
+		g_free(transport);
+		return NULL;
+	}
+
+	return transport;
+}
+
+static void append_byte_array(DBusMessageIter *iter, uint8_t *val, size_t size)
+{
+	DBusMessageIter value;
+	char sig[2] = { DBUS_TYPE_BYTE, '\0' };
+	size_t i;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, sig, &value);
+
+	for (i = 0; i < size; i++)
+		dbus_message_iter_append_basic(&value, DBUS_TYPE_BYTE, &val[i]);
+
+	dbus_message_iter_close_container(iter, &value);
+}
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+					uint8_t **capabilities)
+{
+	*capabilities = endpoint->capabilities;
+	return endpoint->size;
+}
+
+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 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;
+	size_t i = 0;
+
+	/* 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);
+
+		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;
+		uint8_t configuration[128];
+		int perr;
+
+		dbus_message_iter_init(reply, &args);
+
+		i = sizeof(configuration);
+		perr = parse_byte_array(&args, configuration, &i);
+		if (perr < 0) {
+			error("Error parsing configuration: %s", strerror(-perr));
+			i = 0;
+			goto done;
+		}
+
+		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;
+	}
+
+	i = 1;
+	value = TRUE;
+	ret = &value;
+
+done:
+	dbus_message_unref(reply);
+
+	request->cb(endpoint, ret, i, 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;
+
+	debug("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;
+	DBusMessageIter iter;
+	DBusError err;
+
+	if (endpoint->transport != NULL || endpoint->request != NULL)
+		return FALSE;
+
+	conn = endpoint->adapter->conn;
+
+	endpoint->transport = media_transport_create(conn, device);
+	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;
+	}
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					&endpoint->transport->path);
+
+	append_byte_array(&iter, configuration, size);
+
+	if (cb != NULL)
+		return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+
+	dbus_error_init(&err);
+
+	/* FIXME: remove once we can reply setconf asyncronously */
+	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;
+	DBusMessageIter iter;
+
+	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_iter_init_append(msg, &iter);
+
+	append_byte_array(&iter, capabilities, length);
+
+	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(conn, 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);
+}
diff --git a/audio/media.h b/audio/media.h
new file mode 100644
index 0000000..b54a7de
--- /dev/null
+++ b/audio/media.h
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@xxxxxxxxxxxx>
+ *
+ *
+ *  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, size_t size, void *user_data);
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src);
+void media_unregister(const char *path);
+
+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);
diff --git a/audio/sink.c b/audio/sink.c
index 3a8eb23..dc3994e 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,29 @@ 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;
 
-	*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 +374,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 +395,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
 
 	debug("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 1530c34..d9f62f4 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
 
 	debug("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/unix.c b/audio/unix.c
index 5cf4f94..4113a56 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.6.3.3


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux