mpeg12 support patches

"Luiz Augusto von Dentz" <[email protected]>
Newsgroups gmane.linux.bluez.devel
Message-ID <[email protected]>
This patches enable audio service to announce the capabilities of mpeg12
SEPs and use them with gstreamer plugin.

To test them it requires Thiagos's patch against cvs mp3parse:
http://bugzilla.gnome.org/show_bug.cgi?id=504493

then use gstreamer:
gst-launch filesrc location=<some mp3 file> ! mp3parse ! a2dpsink
device=<address>


ps: these changes can also be pulled for my git tree here:
git://git.infradead/users/vudentz/bluez-utils.git

-- 
Luiz Augusto von Dentz
Engenheiro de Computação

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel
0001-Initial-support-of-mpeg12-codec.patch (text/x-diff, 11.8 KB)
From 58d4228ee302d686635fca2edcf5152eedf93766 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Tue, 11 Dec 2007 18:48:41 -0300
Subject: [PATCH] Initial support of mpeg12 codec.

---
 audio/a2dp.c |   30 ++++++++++----------
 audio/a2dp.h |   77 +++++++++++++++++++++++++++++++++++++-------------
 audio/ipc.h  |    8 ++++-
 audio/sink.c |   88 +++++++++++++++++++++++++++++-----------------------------
 audio/unix.c |   19 ++++++++++--
 5 files changed, 139 insertions(+), 83 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 74699cd..1c59374 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -355,25 +355,25 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
 	sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
 
-	sbc_cap.frequency = ( A2DP_SAMPLING_FREQ_48000 |
-				A2DP_SAMPLING_FREQ_44100 |
-				A2DP_SAMPLING_FREQ_32000 |
-				A2DP_SAMPLING_FREQ_16000 );
+	sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 |
+				SBC_SAMPLING_FREQ_44100 |
+				SBC_SAMPLING_FREQ_32000 |
+				SBC_SAMPLING_FREQ_16000 );
 
-	sbc_cap.channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO |
-					A2DP_CHANNEL_MODE_STEREO |
-					A2DP_CHANNEL_MODE_DUAL_CHANNEL |
-					A2DP_CHANNEL_MODE_MONO );
+	sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO |
+					SBC_CHANNEL_MODE_STEREO |
+					SBC_CHANNEL_MODE_DUAL_CHANNEL |
+					SBC_CHANNEL_MODE_MONO );
 
-	sbc_cap.block_length = ( A2DP_BLOCK_LENGTH_16 |
-					A2DP_BLOCK_LENGTH_12 |
-					A2DP_BLOCK_LENGTH_8 |
-					A2DP_BLOCK_LENGTH_4 );
+	sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 |
+					SBC_BLOCK_LENGTH_12 |
+					SBC_BLOCK_LENGTH_8 |
+					SBC_BLOCK_LENGTH_4 );
 
-	sbc_cap.subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 );
+	sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 );
 
-	sbc_cap.allocation_method = ( A2DP_ALLOCATION_LOUDNESS |
-					A2DP_ALLOCATION_SNR );
+	sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS |
+					SBC_ALLOCATION_SNR );
 
 	sbc_cap.min_bitpool = MIN_BITPOOL;
 	sbc_cap.max_bitpool = MAX_BITPOOL;
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 8227296..cfd1c47 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -27,26 +27,37 @@
 #define A2DP_CODEC_MPEG24		0x02
 #define A2DP_CODEC_ATRAC		0x03
 
-#define A2DP_SAMPLING_FREQ_16000	(1 << 3)
-#define A2DP_SAMPLING_FREQ_32000	(1 << 2)
-#define A2DP_SAMPLING_FREQ_44100	(1 << 1)
-#define A2DP_SAMPLING_FREQ_48000	1
-
-#define A2DP_CHANNEL_MODE_MONO		(1 << 3)
-#define A2DP_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
-#define A2DP_CHANNEL_MODE_STEREO	(1 << 1)
-#define A2DP_CHANNEL_MODE_JOINT_STEREO	1
-
-#define A2DP_BLOCK_LENGTH_4		(1 << 3)
-#define A2DP_BLOCK_LENGTH_8		(1 << 2)
-#define A2DP_BLOCK_LENGTH_12		(1 << 1)
-#define A2DP_BLOCK_LENGTH_16		1
-
-#define A2DP_SUBBANDS_4			(1 << 1)
-#define A2DP_SUBBANDS_8			1
-
-#define A2DP_ALLOCATION_SNR		(1 << 1)
-#define A2DP_ALLOCATION_LOUDNESS	1
+#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_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
@@ -64,6 +75,19 @@ struct sbc_codec_cap {
 	uint8_t max_bitpool;
 } __attribute__ ((packed));
 
+struct mpeg_codec_cap {
+	struct avdtp_media_codec_capability cap;
+	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;
+	uint8_t bitrate0:7;
+	uint8_t vbr:1;
+	uint8_t bitrate1;
+} __attribute__ ((packed));
+
 #elif __BYTE_ORDER == __BIG_ENDIAN
 
 struct sbc_codec_cap {
@@ -77,6 +101,19 @@ struct sbc_codec_cap {
 	uint8_t max_bitpool;
 } __attribute__ ((packed));
 
+struct mpeg_codec_cap {
+	struct avdtp_media_codec_capability cap;
+	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;
+	uint8_t vbr:1;
+	uint8_t bitrate0:7;
+	uint8_t bitrate1;
+} __attribute__ ((packed));
+
 #else
 #error "Unknown byte order"
 #endif
diff --git a/audio/ipc.h b/audio/ipc.h
index 3e1200f..64ed65a 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -162,8 +162,14 @@ typedef struct {
 	uint8_t max_bitpool;
 } __attribute__ ((packed)) sbc_capabilities_t;
 
-/* To be defined */
 typedef struct {
+	uint8_t channel_mode;
+	uint8_t crc;
+	uint8_t layer;
+	uint8_t frequency;
+	uint8_t mpf;
+	uint8_t vbr;
+	uint16_t bitrate;
 } __attribute__ ((packed)) mpeg_capabilities_t;
 
 struct bt_getcapabilities_rsp {
diff --git a/audio/sink.c b/audio/sink.c
index 259abf8..a95c2e4 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -193,28 +193,28 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
 static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
 {
 	switch (freq) {
-	case A2DP_SAMPLING_FREQ_16000:
-	case A2DP_SAMPLING_FREQ_32000:
+	case SBC_SAMPLING_FREQ_16000:
+	case SBC_SAMPLING_FREQ_32000:
 		return 53;
-	case A2DP_SAMPLING_FREQ_44100:
+	case SBC_SAMPLING_FREQ_44100:
 		switch (mode) {
-		case A2DP_CHANNEL_MODE_MONO:
-		case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+		case SBC_CHANNEL_MODE_MONO:
+		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
 			return 31;
-		case A2DP_CHANNEL_MODE_STEREO:
-		case A2DP_CHANNEL_MODE_JOINT_STEREO:
+		case SBC_CHANNEL_MODE_STEREO:
+		case SBC_CHANNEL_MODE_JOINT_STEREO:
 			return 53;
 		default:
 			error("Invalid channel mode %u", mode);
 			return 53;
 		}
-	case A2DP_SAMPLING_FREQ_48000:
+	case SBC_SAMPLING_FREQ_48000:
 		switch (mode) {
-		case A2DP_CHANNEL_MODE_MONO:
-		case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+		case SBC_CHANNEL_MODE_MONO:
+		case SBC_CHANNEL_MODE_DUAL_CHANNEL:
 			return 29;
-		case A2DP_CHANNEL_MODE_STEREO:
-		case A2DP_CHANNEL_MODE_JOINT_STEREO:
+		case SBC_CHANNEL_MODE_STEREO:
+		case SBC_CHANNEL_MODE_JOINT_STEREO:
 			return 51;
 		default:
 			error("Invalid channel mode %u", mode);
@@ -236,58 +236,58 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap,
 	cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
 	cap->cap.media_codec_type = A2DP_CODEC_SBC;
 
-	if (supported->frequency & A2DP_SAMPLING_FREQ_44100)
-		cap->frequency = A2DP_SAMPLING_FREQ_44100;
-	else if (supported->frequency & A2DP_SAMPLING_FREQ_48000)
-		cap->frequency = A2DP_SAMPLING_FREQ_48000;
-	else if (supported->frequency & A2DP_SAMPLING_FREQ_32000)
-		cap->frequency = A2DP_SAMPLING_FREQ_32000;
-	else if (supported->frequency & A2DP_SAMPLING_FREQ_16000)
-		cap->frequency = A2DP_SAMPLING_FREQ_16000;
+	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 & A2DP_CHANNEL_MODE_JOINT_STEREO)
-		cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO;
-	else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO)
-		cap->channel_mode = A2DP_CHANNEL_MODE_STEREO;
-	else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL)
-		cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL;
-	else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO)
-		cap->channel_mode = A2DP_CHANNEL_MODE_MONO;
+	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 & A2DP_BLOCK_LENGTH_16)
-		cap->block_length = A2DP_BLOCK_LENGTH_16;
-	else if (supported->block_length & A2DP_BLOCK_LENGTH_12)
-		cap->block_length = A2DP_BLOCK_LENGTH_12;
-	else if (supported->block_length & A2DP_BLOCK_LENGTH_8)
-		cap->block_length = A2DP_BLOCK_LENGTH_8;
-	else if (supported->block_length & A2DP_BLOCK_LENGTH_4)
-		cap->block_length = A2DP_BLOCK_LENGTH_4;
+	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 & A2DP_SUBBANDS_8)
-		cap->subbands = A2DP_SUBBANDS_8;
-	else if (supported->subbands & A2DP_SUBBANDS_4)
-		cap->subbands = A2DP_SUBBANDS_4;
+	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 & A2DP_ALLOCATION_LOUDNESS)
-		cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
-	else if (supported->allocation_method & A2DP_ALLOCATION_SNR)
-		cap->allocation_method = A2DP_ALLOCATION_SNR;
+	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),
diff --git a/audio/unix.c b/audio/unix.c
index d9f7c67..2226579 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -341,6 +341,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
 	struct bt_getcapabilities_rsp *rsp = (void *) buf;
 	struct a2dp_data *a2dp = &client->d.a2dp;
 	struct sbc_codec_cap *sbc_cap = NULL;
+	struct mpeg_codec_cap *mpeg_cap = NULL;
 	GSList *l;
 
 	if (err)
@@ -364,10 +365,11 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
 
 		codec_cap = (void *) cap->data;
 
-		if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) {
+		if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap)
 			sbc_cap = (void *) codec_cap;
-			break;
-		}
+
+		if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12 && !mpeg_cap)
+			mpeg_cap = (void *) codec_cap;
 	}
 
 	/* endianess prevent direct cast */
@@ -381,6 +383,17 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
 		rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool;
 	}
 
+	if (mpeg_cap) {
+		rsp->mpeg_capabilities.channel_mode = mpeg_cap->channel_mode;
+		rsp->mpeg_capabilities.crc = mpeg_cap->crc;
+		rsp->mpeg_capabilities.layer = mpeg_cap->layer;
+		rsp->mpeg_capabilities.frequency = mpeg_cap->frequency;
+		rsp->mpeg_capabilities.mpf = mpeg_cap->mpf;
+		rsp->mpeg_capabilities.vbr = mpeg_cap->vbr;
+		rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 &
+				mpeg_cap->bitrate0 << 8;
+	}
+
 	unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h);
 
 	return;
-- 
1.5.3.7
0002-Make-a2dpsink-to-act-like-a-bin-and-split-payloader.patch (text/x-diff, 61 KB)
From e01446b589d04325c183b344fd73cdd947af1939 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Wed, 12 Dec 2007 12:05:42 -0300
Subject: [PATCH] Make a2dpsink to act like a bin and split payloader code in another element.

---
 audio/Makefile.am    |    4 +-
 audio/gsta2dpsink.c  | 1042 ++++++++++++--------------------------------------
 audio/gsta2dpsink.h  |   29 +-
 audio/gstbluetooth.c |   20 +-
 audio/gstsbcdec.c    |   17 +-
 audio/gstsbcdec.h    |    2 +
 audio/gstsbcenc.c    |  131 +++----
 audio/gstsbcenc.h    |    2 +
 audio/gstsbcparse.c  |  223 ++++-------
 audio/gstsbcparse.h  |    5 +
 audio/gstsbcutil.c   |  100 +++++
 audio/gstsbcutil.h   |   19 +
 12 files changed, 532 insertions(+), 1062 deletions(-)

diff --git a/audio/Makefile.am b/audio/Makefile.am
index f9284b0..5d838c8 100644
--- a/audio/Makefile.am
+++ b/audio/Makefile.am
@@ -43,11 +43,13 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c \
 				gstsbcenc.h gstsbcenc.c \
 				gstsbcdec.h gstsbcdec.c \
 				gstsbcparse.h gstsbcparse.c \
+				gsta2dpsendersink.h gsta2dpsendersink.c \
 				gsta2dpsink.h gsta2dpsink.c \
 				gstsbcutil.h gstsbcutil.c \
+				gstrtpsbcpay.h gstrtpsbcpay.c \
 				rtp.h ipc.h ipc.c
 libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc
-libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10
+libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@
 endif
 endif
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index 5798bc2..d90909c 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -26,166 +26,67 @@
 #endif
 
 #include <unistd.h>
-#include <sys/un.h>
-#include <sys/socket.h>
-#include <fcntl.h>
 #include <pthread.h>
 
-#include <netinet/in.h>
-
-#include <bluetooth/bluetooth.h>
-
-#include "ipc.h"
-#include "rtp.h"
 #include "gstsbcutil.h"
-
 #include "gsta2dpsink.h"
 
-GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);
-#define GST_CAT_DEFAULT a2dp_sink_debug
-
-#define BUFFER_SIZE 2048
-#define TEMPLATE_MAX_BITPOOL_VALUE 64
+GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
+#define GST_CAT_DEFAULT gst_a2dp_sink_debug
 
-#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START {	\
-		g_mutex_lock (s->sink_lock);		\
-	} G_STMT_END
-
-#define GST_A2DP_SINK_MUTEX_UNLOCK(s) G_STMT_START {	\
-		g_mutex_unlock (s->sink_lock);		\
-	} G_STMT_END
-
-
-struct bluetooth_data {
-	struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */
-	struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */
-	int samples;			/* Number of encoded samples */
-	gchar buffer[BUFFER_SIZE];	/* Codec transfer buffer */
-	gsize count;			/* Codec transfer buffer counter */
-
-	int nsamples;			/* Cumulative number of codec samples */
-	uint16_t seq_num;		/* Cumulative packet sequence */
-	int frame_count;		/* Current frames in buffer*/
-};
-
-
-#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
-#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0)
+#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
+#define TEMPLATE_MAX_BITPOOL_STR "64"
 
 enum {
 	PROP_0,
-	PROP_DEVICE,
+	PROP_DEVICE
 };
 
-GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK);
+GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
 
-static const GstElementDetails a2dp_sink_details =
+static const GstElementDetails gst_a2dp_sink_details =
 	GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
 				"Sink/Audio",
 				"Plays audio to an A2DP device",
 				"Marcel Holtmann <[email protected]>");
 
-static GstStaticPadTemplate a2dp_sink_factory =
+static GstStaticPadTemplate gst_a2dp_sink_factory =
 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
-		GST_STATIC_CAPS("audio/x-sbc, "
+			GST_STATIC_CAPS("audio/x-sbc, "
 				"rate = (int) { 16000, 32000, 44100, 48000 }, "
 				"channels = (int) [ 1, 2 ], "
 				"mode = (string) { mono, dual, stereo, joint }, "
 				"blocks = (int) { 4, 8, 12, 16 }, "
 				"subbands = (int) { 4, 8 }, "
-				"allocation = (string) { snr, loudness },"
-				/* FIXME use constant here */
-				"bitpool = (int) [ 2, 64 ]; "
-				"audio/mpeg, "
-				"mpegversion = (int) 1, "
-				"layer = (int) [ 1, 3 ], "
-				"rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, "
-				"channels = (int) [ 1, 2 ]"));
-
-static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self,
-					const bt_audio_msg_header_t *msg);
-static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self,
-				bt_audio_msg_header_t *outmsg,
-				int expected_type);
-
+				"allocation = (string) { snr, loudness }, "
+				"bitpool = (int) [ 2, "
+				TEMPLATE_MAX_BITPOOL_STR " ]; "
+				));
 
 static void gst_a2dp_sink_base_init(gpointer g_class)
 {
 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
 
+	gst_element_class_set_details(element_class,
+		&gst_a2dp_sink_details);
 	gst_element_class_add_pad_template(element_class,
-		gst_static_pad_template_get(&a2dp_sink_factory));
-
-	gst_element_class_set_details(element_class, &a2dp_sink_details);
-}
-
-static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
-
-	GST_INFO_OBJECT(self, "stop");
-
-	if (self->watch_id != 0) {
-		g_source_remove(self->watch_id);
-		self->watch_id = 0;
-	}
-
-	if (self->stream) {
-		g_io_channel_flush(self->stream, NULL);
-		g_io_channel_close(self->stream);
-		g_io_channel_unref(self->stream);
-		self->stream = NULL;
-	}
-
-	if (self->server) {
-		bt_audio_service_close(g_io_channel_unix_get_fd(self->server));
-		g_io_channel_unref(self->server);
-		self->server = NULL;
-	}
-
-	if (self->data) {
-		g_free(self->data);
-		self->data = NULL;
-	}
-
-	if (self->stream_caps) {
-		gst_caps_unref(self->stream_caps);
-		self->stream_caps = NULL;
-	}
-
-	if (self->dev_caps) {
-		gst_caps_unref(self->dev_caps);
-		self->dev_caps = NULL;
-	}
-
-	return TRUE;
-}
-
-static void gst_a2dp_sink_finalize(GObject *object)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(object);
-
-	if (self->data)
-		gst_a2dp_sink_stop(GST_BASE_SINK(self));
-
-	if (self->device)
-		g_free(self->device);
-
-	g_mutex_free(self->sink_lock);
-
-	G_OBJECT_CLASS(parent_class)->finalize(object);
+		gst_static_pad_template_get(&gst_a2dp_sink_factory));
 }
 
 static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
 					const GValue *value, GParamSpec *pspec)
 {
-	GstA2dpSink *sink = GST_A2DP_SINK(object);
+	GstA2dpSink *self = GST_A2DP_SINK(object);
 
 	switch (prop_id) {
 	case PROP_DEVICE:
-		if (sink->device)
-			g_free(sink->device);
-		sink->device = g_value_dup_string(value);
+		if (self->sink != NULL)
+			gst_a2dp_sender_sink_set_device(self->sink,
+				g_value_get_string(value));
+
+		if (self->device != NULL)
+			g_free(self->device);
+		self->device = g_value_dup_string(value);
 		break;
 
 	default:
@@ -197,11 +98,16 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
 static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
 					GValue *value, GParamSpec *pspec)
 {
-	GstA2dpSink *sink = GST_A2DP_SINK(object);
+	GstA2dpSink *self = GST_A2DP_SINK(object);
+	gchar *device;
 
 	switch (prop_id) {
 	case PROP_DEVICE:
-		g_value_set_string(value, sink->device);
+		if (self->sink != NULL) {
+			device = gst_a2dp_sender_sink_get_device(self->sink);
+			if (device != NULL)
+				g_value_take_string(value, device);
+		}
 		break;
 
 	default:
@@ -210,775 +116,305 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
 	}
 }
 
-static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink)
-{
-	int err, ret;
-
-	ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server));
-
-	if (ret < 0) {
-		err = errno;
-		GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
-				strerror(err), err);
-		return -err;
-	}
-
-	sink->stream = g_io_channel_unix_new(ret);
-	GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);
-
-	return 0;
-}
-
-static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
-					GstCaps *caps,
-					sbc_capabilities_t *pkt)
+static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
+			GstStateChange transition)
 {
-	sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities;
-	const GValue *value = NULL;
-	const char *pref, *name;
-	gint rate, blocks, subbands;
-	GstStructure *structure = gst_caps_get_structure(caps, 0);
-
-	name = gst_structure_get_name(structure);
-	/* FIXME only sbc supported here, should suport mp3 */
-	if (!(IS_SBC(name))) {
-		GST_ERROR_OBJECT(sink, "Unsupported format %s", name);
-		return FALSE;
-	}
-
-	value = gst_structure_get_value(structure, "rate");
-	rate = g_value_get_int(value);
-	if (rate == 44100)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100;
-	else if (rate == 48000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000;
-	else if (rate == 32000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000;
-	else if (rate == 16000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000;
-	else {
-		GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
-		return FALSE;
-	}
-
-	value = gst_structure_get_value(structure, "mode");
-	pref = g_value_get_string(value);
-	if (strcmp(pref, "auto") == 0)
-		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;
-	else if (strcmp(pref, "mono") == 0)
-		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
-	else if (strcmp(pref, "dual") == 0)
-		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
-	else if (strcmp(pref, "stereo") == 0)
-		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
-	else if (strcmp(pref, "joint") == 0)
-		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
-	else {
-		GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);
-		return FALSE;
-	}
+	GstA2dpSink *self = GST_A2DP_SINK(element);
 
-	value = gst_structure_get_value(structure, "allocation");
-	pref = g_value_get_string(value);
-	if (strcmp(pref, "auto") == 0)
-		cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO;
-	else if (strcmp(pref, "loudness") == 0)
-		cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
-	else if (strcmp(pref, "snr") == 0)
-		cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
-	else {
-		GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);
-		return FALSE;
-	}
+	switch (transition) {
+	case GST_STATE_CHANGE_NULL_TO_READY:
+		gst_element_set_state(GST_ELEMENT(self->sink),
+			GST_STATE_READY);
+		break;
 
-	value = gst_structure_get_value(structure, "subbands");
-	subbands = g_value_get_int(value);
-	if (subbands == 8)
-		cfg->subbands = BT_A2DP_SUBBANDS_8;
-	else if (subbands == 4)
-		cfg->subbands = BT_A2DP_SUBBANDS_4;
-	else {
-		GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands);
-		return FALSE;
-	}
+	case GST_STATE_CHANGE_READY_TO_NULL:
+		if (self->newseg_event != NULL) {
+			gst_event_unref(self->newseg_event);
+			self->newseg_event = NULL;
+		}
+		break;
 
-	value = gst_structure_get_value(structure, "blocks");
-	blocks = g_value_get_int(value);
-	if (blocks == 16)
-		cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
-	else if (blocks == 12)
-		cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
-	else if (blocks == 8)
-		cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
-	else if (blocks == 4)
-		cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
-	else {
-		GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks);
-		return FALSE;
+	default:
+		break;
 	}
 
-	/* FIXME min and max ??? */
-	value = gst_structure_get_value(structure, "bitpool");
-
-	cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
-
-	memcpy(pkt, cfg, sizeof(*pkt));
-
-	return TRUE;
+	return GST_ELEMENT_CLASS(parent_class)->change_state(element,
+			transition);
 }
 
-static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self)
+static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
 {
-	struct bluetooth_data *data = self->data;
-	gint ret;
-	GIOError err;
-	GError *gerr = NULL;
-	GIOStatus status;
-	GIOFlags flags;
-	gsize read;
-
-	ret = gst_a2dp_sink_bluetooth_recvmsg_fd(self);
-	if (ret < 0)
-		return FALSE;
-
-	if (!self->stream) {
-		GST_ERROR_OBJECT(self, "Error while configuring device: "
-				"could not acquire audio socket");
-		return FALSE;
-	}
+	GObjectClass *object_class = G_OBJECT_CLASS(klass);
+	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
 
-	/* set stream socket to nonblock */
-	GST_LOG_OBJECT(self, "setting stream socket to nonblock");
-	flags = g_io_channel_get_flags(self->stream);
-	flags |= G_IO_FLAG_NONBLOCK;
-	status = g_io_channel_set_flags(self->stream, flags, &gerr);
-	if (status != G_IO_STATUS_NORMAL) {
-		if (gerr)
-			GST_WARNING_OBJECT(self, "Error while "
-				"setting server socket to nonblock: "
-				"%s", gerr->message);
-		else
-			GST_WARNING_OBJECT(self, "Error while "
-				"setting server "
-				"socket to nonblock");
-	}
+	parent_class = g_type_class_peek_parent(klass);
 
-	/* It is possible there is some outstanding
-	data in the pipe - we have to empty it */
-	GST_LOG_OBJECT(self, "emptying stream pipe");
-	while (1) {
-		err = g_io_channel_read(self->stream, data->buffer,
-					(gsize) data->cfg.link_mtu,
-					&read);
-		if (err != G_IO_ERROR_NONE || read <= 0)
-			break;
-	}
+	object_class->set_property = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sink_set_property);
+	object_class->get_property = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sink_get_property);
 
-	/* set stream socket to block */
-	GST_LOG_OBJECT(self, "setting stream socket to block");
-	flags = g_io_channel_get_flags(self->stream);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	status = g_io_channel_set_flags(self->stream, flags, &gerr);
-	if (status != G_IO_STATUS_NORMAL) {
-		if (gerr)
-			GST_WARNING_OBJECT(self, "Error while "
-				"setting server socket to block:"
-				"%s", gerr->message);
-		else
-			GST_WARNING_OBJECT(self, "Error while "
-				"setting server "
-				"socket to block");
-	}
+	element_class->change_state = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sink_change_state);
 
-	memset(data->buffer, 0, sizeof(data->buffer));
+	g_object_class_install_property(object_class, PROP_DEVICE,
+			g_param_spec_string("device", "Device",
+			"Bluetooth remote device address",
+			NULL, G_PARAM_READWRITE));
 
-	return TRUE;
+	GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
+				"A2DP sink element");
 }
 
-static gboolean server_callback(GIOChannel *chan,
-					GIOCondition cond, gpointer data)
+static GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
 {
-	GstA2dpSink *sink;
-
-	if (cond & G_IO_HUP || cond & G_IO_NVAL)
-		return FALSE;
-	else if (cond & G_IO_ERR) {
-		sink = GST_A2DP_SINK(data);
-		GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR");
-	}
-
-	return TRUE;
+	return gst_a2dp_sender_sink_get_device_caps(self->sink);
 }
 
-static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
 {
-	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
-	GstStructure *structure;
-	GValue *value;
-	GValue *list;
-	gchar *tmp;
-
-	GST_LOG_OBJECT(self, "updating device caps");
-
-	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_AUTO) {
-		g_value_set_static_string(value, "joint");
-		gst_value_list_prepend_value(list, value);
-		g_value_set_static_string(value, "stereo");
-		gst_value_list_prepend_value(list, value);
-		g_value_set_static_string(value, "mono");
-		gst_value_list_prepend_value(list, value);
-		g_value_set_static_string(value, "dual");
-		gst_value_list_prepend_value(list, value);
-	} else {
-		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_AUTO) {
-		g_value_set_static_string(value, "loudness");
-		gst_value_list_prepend_value(list, value);
-		g_value_set_static_string(value, "snr");
-		gst_value_list_prepend_value(list, value);
+	GstCaps *caps;
+	GstCaps *caps_aux;
+	GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+
+	if (self->sink == NULL) {
+		GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
+			"returning template caps");
+		caps = gst_static_pad_template_get_caps(
+				&gst_a2dp_sink_factory);
 	} else {
-		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_A2DP_SAMPLING_FREQ_48000) {
-		g_value_set_int(value, 48000);
-		gst_value_list_prepend_value(list, value);
-	}
-	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) {
-		g_value_set_int(value, 44100);
-		gst_value_list_prepend_value(list, value);
-	}
-	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) {
-		g_value_set_int(value, 32000);
-		gst_value_list_prepend_value(list, value);
-	}
-	if (sbc->frequency & BT_A2DP_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_VALUE),
-			MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE));
-	gst_structure_set_value(structure, "bitpool", value);
-
-	/* channels */
-	gst_value_set_int_range(value, 1, 2);
-	gst_structure_set_value(structure, "channels", value);
-
-	g_free(value);
-
-	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, "Device capabilities: %s", tmp);
-	g_free(tmp);
-
-	return TRUE;
+		GST_LOG_OBJECT(self, "Getting device caps");
+		caps = gst_a2dp_sink_get_device_caps(self);
+		if (caps == NULL)
+			caps = gst_static_pad_template_get_caps(
+                                &gst_a2dp_sink_factory);
+	}
+	caps_aux = gst_caps_copy(caps);
+	g_object_set(self->capsfilter, "caps", caps_aux, NULL);
+	gst_caps_unref(caps_aux);
+	return caps;
 }
 
-static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)
+static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self)
 {
-	gchar *buf[BT_AUDIO_IPC_PACKET_SIZE];
-	struct bt_getcapabilities_req *req = (void *) buf;
-	bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf;
-	struct bt_getcapabilities_rsp *rsp = (void *) buf;
-	GIOError io_error;
-
-	memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+	GstElement *sink;
 
-	req->h.msg_type = BT_GETCAPABILITIES_REQ;
-	strncpy(req->device, self->device, 18);
-
-	io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while asking device caps");
-	}
+	if (self->sink == NULL)
+		sink = gst_element_factory_make("a2dpsendersink", "sendersink");
+	else
+		sink = GST_ELEMENT(self->sink);
 
-	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h,
-			BT_GETCAPABILITIES_RSP);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while getting device caps");
+	if (sink == NULL) {
+		GST_ERROR_OBJECT(self, "Couldn't create a2dpsendersink");
 		return FALSE;
 	}
 
-	if (rsp_hdr->posix_errno != 0) {
-		GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)",
-					strerror(rsp_hdr->posix_errno),
-					rsp_hdr->posix_errno);
-		return FALSE;
+	if (!gst_bin_add(GST_BIN(self), sink)) {
+		GST_ERROR_OBJECT(self, "failed to add a2dpsendersink "
+			"to the bin");
+		goto cleanup_and_fail;
 	}
 
-	memcpy(&self->data->caps, rsp, sizeof(*rsp));
-	if (!gst_a2dp_sink_update_caps(self)) {
-		GST_WARNING_OBJECT(self, "failed to update capabilities");
-		return FALSE;
+	if (gst_element_set_state(sink, GST_STATE_READY) ==
+			GST_STATE_CHANGE_FAILURE) {
+		GST_ERROR_OBJECT(self, "a2dpsendersink failed to go to ready");
+		goto remove_element_and_fail;
 	}
 
-	return TRUE;
-}
-
-static gboolean gst_a2dp_sink_start(GstBaseSink *basesink)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
-	gint sk;
-	gint err;
-
-	GST_INFO_OBJECT(self, "start");
-
-	self->watch_id = 0;
-
-	sk = bt_audio_service_open();
-	if (sk <= 0) {
-		err = errno;
-		GST_ERROR_OBJECT(self, "Cannot open connection to bt "
-			"audio service: %s %d", strerror(err), err);
-		goto failed;
+	if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
+		GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
+			"to a2dpsendersink");
+		goto remove_element_and_fail;
 	}
 
-	self->server = g_io_channel_unix_new(sk);
-	self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
-					G_IO_NVAL, server_callback, self);
+	self->sink = GST_A2DP_SENDER_SINK(sink);
+	g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
 
-	self->data = g_new0(struct bluetooth_data, 1);
-	memset(self->data, 0, sizeof(struct bluetooth_data));
+	gst_element_set_state(sink, GST_STATE_PAUSED);
 
-	self->stream = NULL;
-	self->stream_caps = NULL;
+	return TRUE;
 
-	if (!gst_a2dp_sink_get_capabilities(self))
-		goto failed;
+remove_element_and_fail:
+	gst_element_set_state (sink, GST_STATE_NULL);
+	gst_bin_remove(GST_BIN(self), sink);
+	return FALSE;
 
-	return TRUE;
+cleanup_and_fail:
+	if (sink != NULL)
+		g_object_unref(G_OBJECT(sink));
 
-failed:
-	bt_audio_service_close(sk);
 	return FALSE;
 }
 
-static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self)
+static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
 {
-	gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
-	struct bt_streamstart_req *req = (void *) buf;
-	bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf;
-	struct bt_streamfd_ind *ind = (void*) buf;
-	GIOError io_error;
-
-	GST_DEBUG_OBJECT(self, "stream start");
-
-	memset (req, 0, sizeof(buf));
-	req->h.msg_type = BT_STREAMSTART_REQ;
-
-	io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error ocurred while sending "
-					"start packet");
-		return FALSE;
-	}
-
-	GST_DEBUG_OBJECT(self, "stream start packet sent");
-
-	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h,
-			BT_STREAMSTART_RSP);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while stream start confirmation");
-		return FALSE;
-	}
-
-	if (rsp_hdr->posix_errno != 0) {
-		GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)",
-					strerror(rsp_hdr->posix_errno),
-					rsp_hdr->posix_errno);
-		return FALSE;
-	}
-
-	GST_DEBUG_OBJECT(self, "stream started");
+	GstElement *rtppay;
 
-	io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h,
-			BT_STREAMFD_IND);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while receiving stream fd");
+	rtppay = gst_element_factory_make("rtpsbcpay", "rtp");
+	if (rtppay == NULL) {
+		GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay");
 		return FALSE;
 	}
 
-	if (!gst_a2dp_sink_conf_recv_stream_fd(self))
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)
-{
-	gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
-	struct bt_setconfiguration_req *req = (void *) buf;
-	bt_audio_rsp_msg_header_t *rsp_hdr  = (void *) buf;
-	struct bt_setconfiguration_rsp *rsp = (void *) buf;
-	gboolean ret;
-	GIOError io_error;
-
-	GST_DEBUG_OBJECT(self, "configuring device");
-
-	memset (req, 0, sizeof(buf));
-	req->h.msg_type = BT_SETCONFIGURATION_REQ;
-	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
-	strncpy(req->device, self->device, 18);
-	ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities);
-	if (!ret) {
-		GST_ERROR_OBJECT(self, "Couldn't parse caps "
-				"to packet configuration");
-		return FALSE;
+	if (!gst_bin_add(GST_BIN(self), rtppay)) {
+		GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin");
+		goto cleanup_and_fail;
 	}
 
-	io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error ocurred while sending "
-					"configurarion packet");
-		return FALSE;
+	if (gst_element_set_state(rtppay, GST_STATE_READY) ==
+			GST_STATE_CHANGE_FAILURE) {
+		GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready");
+		goto remove_element_and_fail;
 	}
 
-	GST_DEBUG_OBJECT(self, "configuration packet sent");
-
-	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h,
-			BT_SETCONFIGURATION_RSP);
-	if (io_error != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while receiving device confirmation");
-		return FALSE;
+	if (!gst_element_link(self->capsfilter, rtppay)) {
+		GST_ERROR_OBJECT(self, "couldn't link capsfilter "
+			"to rtpsbcpay");
+		goto remove_element_and_fail;
 	}
 
-	if (rsp_hdr->posix_errno != 0) {
-		GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)",
-					strerror(rsp_hdr->posix_errno),
-					rsp_hdr->posix_errno);
-		return FALSE;
-	}
+	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+	g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
 
-	memcpy(&self->data->cfg, rsp, sizeof(*rsp));
-	GST_DEBUG_OBJECT(self, "configuration set");
+	gst_element_set_state(rtppay, GST_STATE_PAUSED);
 
 	return TRUE;
-}
 
-static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink,
-					GstBuffer *buffer)
-{
-	GstA2dpSink *sink = GST_A2DP_SINK(basesink);
-	gboolean ret;
-
-	GST_A2DP_SINK_MUTEX_LOCK(sink);
-
-	ret = gst_a2dp_sink_stream_start(sink);
-
-	GST_A2DP_SINK_MUTEX_UNLOCK(sink);
+remove_element_and_fail:
+	gst_element_set_state (rtppay, GST_STATE_NULL);
+	gst_bin_remove(GST_BIN(self), rtppay);
+	return FALSE;
 
-	if (!ret)
-		return GST_FLOW_ERROR;
+cleanup_and_fail:
+	if (rtppay != NULL)
+		g_object_unref(G_OBJECT(rtppay));
 
-	return GST_FLOW_OK;
+	return FALSE;
 }
 
-static int gst_a2dp_sink_avdtp_write(GstA2dpSink *self)
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 {
-	gsize ret;
-	struct bluetooth_data *data = self->data;
-	struct rtp_header *header;
-	struct rtp_payload *payload;
-	GIOError err;
-
-	header = (void *) data->buffer;
-	payload = (void *) (data->buffer + sizeof(*header));
-
-	memset(data->buffer, 0, sizeof(*header) + sizeof(*payload));
-
-	payload->frame_count = data->frame_count;
-	header->v = 2;
-	header->pt = 1;
-	header->sequence_number = htons(data->seq_num);
-	header->timestamp = htonl(data->nsamples);
-	header->ssrc = htonl(1);
-
-	err = g_io_channel_write(self->stream, data->buffer, data->count, &ret);
-	if (err != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error while sending data");
-		ret = -1;
-	}
-
-	/* Reset buffer of data to send */
-	data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
-	data->frame_count = 0;
-	data->samples = 0;
-	data->seq_num++;
-
-	return ret;
-}
+	GstA2dpSink *self;
+	GstStructure *structure;
 
-static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink,
-					GstBuffer *buffer)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
-	struct bluetooth_data *data = self->data;
-	gint encoded;
-	gint ret;
+	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+	GST_INFO_OBJECT(self, "setting caps");
 
-	encoded = GST_BUFFER_SIZE(buffer);
+	structure = gst_caps_get_structure(caps, 0);
 
-	if (data->count + encoded >= data->cfg.link_mtu) {
-		ret = gst_a2dp_sink_avdtp_write(self);
-		if (ret < 0)
-			return GST_FLOW_ERROR;
+	/* first, we need to create our rtp payloader */
+	if (gst_structure_has_name(structure, "audio/x-sbc"))
+		gst_a2dp_sink_init_rtp_sbc_element(self);
+	else {
+		GST_ERROR_OBJECT(self, "Unexpected media type");
+		return FALSE;
 	}
 
-	memcpy(data->buffer + data->count, GST_BUFFER_DATA(buffer), encoded);
-	data->count += encoded;
-	data->frame_count++;
-
-	return GST_FLOW_OK;
-}
-
-static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
-
-	if (self->dev_caps)
-		return gst_caps_ref(self->dev_caps);
-
-	return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self)));
-}
-
-static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
-	gboolean ret;
-
-	GST_A2DP_SINK_MUTEX_LOCK(self);
-	ret = gst_a2dp_sink_configure(self, caps);
-
-	if (self->stream_caps)
-		gst_caps_unref(self->stream_caps);
-	self->stream_caps = gst_caps_ref(caps);
-
-	GST_A2DP_SINK_MUTEX_UNLOCK(self);
+	if (!gst_a2dp_sink_init_sender_sink(self))
+		return FALSE;
 
-	return ret;
-}
+	if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps))
+		return FALSE;
 
-static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink)
-{
-	GstA2dpSink *self = GST_A2DP_SINK(basesink);
+	g_object_set(G_OBJECT(self->rtp), "mtu",
+		gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL);
 
-	if (self->stream != NULL)
-		g_io_channel_flush (self->stream, NULL);
+	/* we forward our new segment here if we have one */
+	gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
+				self->newseg_event);
+	self->newseg_event = NULL;
 
-	return TRUE;
+	return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
 }
 
-static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
+/* used for catching newsegment events while we don't have a sink, for
+ * later forwarding it to the sink */
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS(klass);
-	GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass);
+	GstA2dpSink *self;
 
-	parent_class = g_type_class_peek_parent(klass);
+	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
 
-	object_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize);
-	object_class->set_property = GST_DEBUG_FUNCPTR(
-					gst_a2dp_sink_set_property);
-	object_class->get_property = GST_DEBUG_FUNCPTR(
-					gst_a2dp_sink_get_property);
-
-	basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sink_start);
-	basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sink_stop);
-	basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render);
-	basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll);
-	basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps);
-	basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps);
-	basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock);
-
-	g_object_class_install_property(object_class, PROP_DEVICE,
-					g_param_spec_string("device", "Device",
-					"Bluetooth remote device address",
-					NULL, G_PARAM_READWRITE));
+	if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
+			gst_element_get_parent(GST_ELEMENT(self->sink)) !=
+			GST_OBJECT_CAST(self)) {
+		if (self->newseg_event != NULL)
+			gst_event_unref(self->newseg_event);
+		self->newseg_event = gst_event_ref(event);
+	}
 
-	GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0,
-				"A2DP sink element");
+	return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
 }
 
-static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass)
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
 {
-	self->device = NULL;
-	self->data = NULL;
-
-	self->stream = NULL;
+	GstElement *element;
 
-	self->dev_caps = NULL;
+	element = gst_element_factory_make("capsfilter", "filter");
+	if (element == NULL)
+		goto failed;
 
-	self->sink_lock = g_mutex_new();
-}
+	if (!gst_bin_add(GST_BIN(self), element))
+		goto failed;
 
-static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self,
-					const bt_audio_msg_header_t *msg)
-{
-	gint err;
-	GIOError error;
-	gsize written;
-
-	GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type));
-
-	error = g_io_channel_write(self->server, (const gchar*) msg,
-			BT_AUDIO_IPC_PACKET_SIZE, &written);
-	if (error != G_IO_ERROR_NONE) {
-		err = errno;
-		GST_ERROR_OBJECT(self, "Error sending data to audio service:"
-			" %s(%d)", strerror(err), err);
-	}
+	self->capsfilter = element;
+	return TRUE;
 
-	return error;
+failed:
+	GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
+	return FALSE;
 }
 
-static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self,
-					bt_audio_msg_header_t *inmsg)
+static void gst_a2dp_sink_init(GstA2dpSink *self,
+			GstA2dpSinkClass *klass)
 {
-	GIOError status;
-	gsize bytes_read;
-	const char *type;
-
-	status = g_io_channel_read(self->server, (gchar*) inmsg,
-			BT_AUDIO_IPC_PACKET_SIZE, &bytes_read);
-	if (status != G_IO_ERROR_NONE) {
-		GST_ERROR_OBJECT(self, "Error receiving data from service");
-		return status;
-	}
-
-	type = bt_audio_strmsg(inmsg->msg_type);
-	if (!type) {
-		GST_ERROR_OBJECT(self, "Bogus message type %d "
-				"received from audio service",
-				inmsg->msg_type);
-		return G_IO_ERROR_INVAL;
-	}
-
-	GST_DEBUG_OBJECT(self, "Received %s", type);
+	GstPad *capsfilter_pad;
 
-	return status;
+	self->sink = NULL;
+	self->rtp = NULL;
+	self->device = NULL;
+	self->capsfilter = NULL;
+	self->newseg_event = NULL;
+
+	/* we initialize our capsfilter */
+	gst_a2dp_sink_init_caps_filter(self);
+	g_object_set(self->capsfilter, "caps",
+		gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
+		NULL);
+
+	/* we search for the capsfilter sinkpad */
+	capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
+
+	/* now we add a ghostpad */
+	self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
+		capsfilter_pad));
+	g_object_unref(capsfilter_pad);
+
+	/* the getcaps of our ghostpad must reflect the device caps */
+	gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
+				gst_a2dp_sink_get_caps);
+	self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
+	gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
+			GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
+
+	/* we need to handle events on our own and we also need the eventfunc
+	 * of the ghostpad for forwarding calls */
+	self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
+	gst_pad_set_event_function(GST_PAD(self->ghostpad),
+			gst_a2dp_sink_handle_event);
+
+	if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
+		GST_ERROR_OBJECT(self, "failed to add ghostpad");
+
+	self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make(
+			"a2dpsendersink", "sendersink"));
+	if (self->sink == NULL)
+		GST_WARNING_OBJECT(self, "failed to create a2dpsendersink");
 }
 
-static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self,
-					bt_audio_msg_header_t *outmsg,
-					int expected_type)
+gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin)
 {
-	GIOError status;
-
-	status = gst_a2dp_sink_audioservice_recv(self, outmsg);
-	if (status != G_IO_ERROR_NONE)
-		return status;
-
-	if (outmsg->msg_type != expected_type) {
-		GST_ERROR_OBJECT(self, "Bogus message %s "
-				"received while %s was expected",
-				bt_audio_strmsg(outmsg->msg_type),
-				bt_audio_strmsg(expected_type));
-		return G_IO_ERROR_INVAL;
-	}
-
-	return status;
+	return gst_element_register (plugin, "a2dpsink",
+			GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK);
 }
 
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index f5b9b69..4bf9d60 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -22,7 +22,8 @@
  */
 
 #include <gst/gst.h>
-#include <gst/base/gstbasesink.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include "gsta2dpsendersink.h"
 
 G_BEGIN_DECLS
 
@@ -40,31 +41,27 @@ G_BEGIN_DECLS
 typedef struct _GstA2dpSink GstA2dpSink;
 typedef struct _GstA2dpSinkClass GstA2dpSinkClass;
 
-struct bluetooth_data;
-
 struct _GstA2dpSink {
-	GstBaseSink sink;
-
-	gchar *device;
-	GIOChannel *stream;
+	GstBin bin;
 
-	struct bluetooth_data *data;
-	GIOChannel *server;
+	GstBaseRTPPayload *rtp;
+	GstA2dpSenderSink *sink;
+	GstElement *capsfilter;
 
-	/* stream connection data */
-	GstCaps *stream_caps;
-
-	GstCaps *dev_caps;
+	gchar *device;
 
-	GMutex *sink_lock;
+	GstGhostPad *ghostpad;
+	GstPadSetCapsFunction ghostpad_setcapsfunc;
+	GstPadEventFunction ghostpad_eventfunc;
 
-	guint watch_id;
+	GstEvent *newseg_event;
 };
 
 struct _GstA2dpSinkClass {
-	GstBaseSinkClass parent_class;
+	GstBinClass parent_class;
 };
 
 GType gst_a2dp_sink_get_type(void);
+gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin);
 
 G_END_DECLS
diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c
index 593a311..764bc89 100644
--- a/audio/gstbluetooth.c
+++ b/audio/gstbluetooth.c
@@ -28,7 +28,9 @@
 #include "gstsbcenc.h"
 #include "gstsbcdec.h"
 #include "gstsbcparse.h"
+#include "gsta2dpsendersink.h"
 #include "gsta2dpsink.h"
+#include "gstrtpsbcpay.h"
 
 static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc");
 
@@ -55,20 +57,22 @@ static gboolean plugin_init(GstPlugin *plugin)
 					SBC_CAPS, NULL, NULL) == FALSE)
 		return FALSE;
 
-	if (gst_element_register(plugin, "sbcenc",
-			GST_RANK_NONE, GST_TYPE_SBC_ENC) == FALSE)
+	if (!gst_sbc_enc_plugin_init(plugin))
 		return FALSE;
 
-	if (gst_element_register(plugin, "sbcdec",
-			GST_RANK_PRIMARY, GST_TYPE_SBC_DEC) == FALSE)
+	if (!gst_sbc_dec_plugin_init(plugin))
 		return FALSE;
 
-	if (gst_element_register(plugin, "sbcparse",
-			GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE)
+	if (!gst_sbc_parse_plugin_init(plugin))
 		return FALSE;
 
-	if (gst_element_register(plugin, "a2dpsink",
-			GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE)
+	if (!gst_a2dp_sender_sink_plugin_init(plugin))
+		return FALSE;
+
+	if (!gst_a2dp_sink_plugin_init(plugin))
+		return FALSE;
+
+	if (!gst_rtp_sbc_pay_plugin_init(plugin))
 		return FALSE;
 
 	return TRUE;
diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c
index a60c3e6..8c27dab 100644
--- a/audio/gstsbcdec.c
+++ b/audio/gstsbcdec.c
@@ -188,10 +188,21 @@ static void gst_sbc_dec_class_init(GstSbcDecClass *klass)
 
 static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass)
 {
-	self->sinkpad = gst_pad_new_from_static_template(&sbc_dec_sink_factory, "sink");
-	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_dec_chain));
+	self->sinkpad = gst_pad_new_from_static_template(
+			&sbc_dec_sink_factory, "sink");
+	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(
+			sbc_dec_chain));
 	gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
 
-	self->srcpad = gst_pad_new_from_static_template(&sbc_dec_src_factory, "src");
+	self->srcpad = gst_pad_new_from_static_template(
+			&sbc_dec_src_factory, "src");
 	gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
 }
+
+gboolean gst_sbc_dec_plugin_init (GstPlugin * plugin)
+{
+	return gst_element_register (plugin, "sbcdec",
+			GST_RANK_PRIMARY, GST_TYPE_SBC_DEC);
+}
+
+
diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h
index 4a6922a..0bb0b57 100644
--- a/audio/gstsbcdec.h
+++ b/audio/gstsbcdec.h
@@ -58,4 +58,6 @@ struct _GstSbcDecClass {
 
 GType gst_sbc_dec_get_type(void);
 
+gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin);
+
 G_END_DECLS
diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c
index 185151f..08ddc14 100644
--- a/audio/gstsbcenc.c
+++ b/audio/gstsbcenc.c
@@ -120,32 +120,6 @@ static GstStaticPadTemplate sbc_enc_src_factory =
 
 gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps);
 
-static void sbc_enc_set_structure_int_param(GstSbcEnc *enc,
-			GstStructure *structure, const gchar* field,
-			gint field_value)
-{
-	GValue *value;
-
-	value = g_new0(GValue,1);
-	value = g_value_init(value, G_TYPE_INT);
-	g_value_set_int(value, field_value);
-	gst_structure_set_value(structure, field, value);
-	g_free(value);
-}
-
-static void sbc_enc_set_structure_string_param(GstSbcEnc *enc,
-			GstStructure *structure, const gchar* field,
-			const gchar* field_value)
-{
-	GValue *value;
-
-	value = g_new0(GValue,1);
-	value = g_value_init(value, G_TYPE_STRING);
-	g_value_set_string(value, field_value);
-	gst_structure_set_value(structure, field, value);
-	g_free(value);
-}
-
 static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc)
 {
 	GstCaps* src_caps;
@@ -153,45 +127,49 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc)
 	GEnumValue *enum_value;
 	GEnumClass *enum_class;
 	gchar* temp;
+	GValue *value;
 
 	src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad));
 	structure = gst_caps_get_structure(src_caps, 0);
 
+	value = g_new0(GValue, 1);
+
 	if (enc->rate != 0)
-		sbc_enc_set_structure_int_param(enc, structure, "rate",
-			enc->rate);
+		gst_sbc_util_set_structure_int_param(structure, "rate",
+			enc->rate, value);
 
 	if (enc->channels != 0)
-		sbc_enc_set_structure_int_param(enc, structure, "channels",
-			enc->channels);
+		gst_sbc_util_set_structure_int_param(structure, "channels",
+			enc->channels, value);
 
 	if (enc->subbands != 0)
-		sbc_enc_set_structure_int_param(enc, structure, "subbands",
-			enc->subbands);
+		gst_sbc_util_set_structure_int_param(structure, "subbands",
+			enc->subbands, value);
 
 	if (enc->blocks != 0)
-		sbc_enc_set_structure_int_param(enc, structure, "blocks",
-			enc->blocks);
+		gst_sbc_util_set_structure_int_param(structure, "blocks",
+			enc->blocks, value);
 
 	if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) {
 		enum_class = g_type_class_ref(GST_TYPE_SBC_MODE);
 		enum_value = g_enum_get_value(enum_class, enc->mode);
-		sbc_enc_set_structure_string_param(enc, structure, "mode",
-			enum_value->value_nick);
+		gst_sbc_util_set_structure_string_param(structure, "mode",
+			enum_value->value_nick, value);
 		g_type_class_unref(enum_class);
 	}
 
 	if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) {
 		enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION);
 		enum_value = g_enum_get_value(enum_class, enc->allocation);
-		sbc_enc_set_structure_string_param(enc, structure, "allocation",
-			enum_value->value_nick);
+		gst_sbc_util_set_structure_string_param(structure, "allocation",
+			enum_value->value_nick, value);
 		g_type_class_unref(enum_class);
 	}
 
 	temp = gst_caps_to_string(src_caps);
 	GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp);
 	g_free(temp);
+	g_free(value);
 
 	return src_caps;
 }
@@ -207,23 +185,10 @@ static GstCaps* sbc_enc_src_getcaps (GstPad * pad)
 
 static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps)
 {
-	GstCaps* srcpad_caps;
-	GstCaps* temp_caps;
-	gboolean res = TRUE;
 	GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
 
 	GST_LOG_OBJECT(enc, "setting srcpad caps");
 
-	srcpad_caps = sbc_enc_generate_srcpad_caps(enc);
-	temp_caps = gst_caps_intersect(srcpad_caps, caps);
-	if (temp_caps == GST_CAPS_NONE)
-		res = FALSE;
-
-	gst_caps_unref(temp_caps);
-	gst_caps_unref(srcpad_caps);
-
-	g_return_val_if_fail(res, FALSE);
-
 	return gst_sbc_enc_fill_sbc_params(enc, caps);
 }
 
@@ -313,38 +278,16 @@ error:
 
 gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps)
 {
-	GstStructure *structure;
-	gint rate, channels, subbands, blocks, bitpool;
-	const gchar* mode;
-	const gchar* allocation;
-
-	g_assert(gst_caps_is_fixed(caps));
 
-	structure = gst_caps_get_structure(caps, 0);
-
-	if (!gst_structure_get_int(structure, "rate", &rate))
-		return FALSE;
-	if (!gst_structure_get_int(structure, "channels", &channels))
-		return FALSE;
-	if (!gst_structure_get_int(structure, "subbands", &subbands))
-		return FALSE;
-	if (!gst_structure_get_int(structure, "blocks", &blocks))
-		return FALSE;
-	if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+	if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps))
 		return FALSE;
 
-	if (!(mode = gst_structure_get_string(structure, "mode")))
-		return FALSE;
-	if (!(allocation = gst_structure_get_string(structure, "allocation")))
-		return FALSE;
-
-	enc->rate = enc->sbc.rate = rate;
-	enc->channels = enc->sbc.channels = channels;
-	enc->blocks = enc->sbc.blocks = blocks;
-	enc->subbands = enc->sbc.subbands = subbands;
-	enc->sbc.bitpool = bitpool;
-	enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode);
-	enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation);
+	enc->rate = enc->sbc.rate;
+	enc->channels = enc->sbc.channels;
+	enc->blocks = enc->sbc.blocks;
+	enc->subbands = enc->sbc.subbands;
+	enc->mode = enc->sbc.joint;
+	enc->allocation = enc->sbc.allocation;
 	enc->codesize = sbc_get_codesize(&enc->sbc);
 	enc->frame_length = sbc_get_frame_length(&enc->sbc);
 	enc->frame_duration = sbc_get_frame_duration(&enc->sbc);
@@ -390,6 +333,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer)
 		gst_adapter_flush(adapter, consumed);
 
 		GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer);
+		/* we have only 1 frame */
+		GST_BUFFER_DURATION(output) = enc->frame_duration;
 
 		res = gst_pad_push(enc->srcpad, output);
 		if (res != GST_FLOW_OK)
@@ -559,17 +504,22 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass)
 
 static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass)
 {
-	self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink");
+	self->sinkpad = gst_pad_new_from_static_template(
+		&sbc_enc_sink_factory, "sink");
 	gst_pad_set_setcaps_function (self->sinkpad,
 			GST_DEBUG_FUNCPTR (sbc_enc_sink_setcaps));
 	gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
 
-	self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src");
-	gst_pad_set_getcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps));
-	gst_pad_set_setcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps));
+	self->srcpad = gst_pad_new_from_static_template(
+		&sbc_enc_src_factory, "src");
+	gst_pad_set_getcaps_function(self->srcpad,
+		GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps));
+	gst_pad_set_setcaps_function(self->srcpad,
+		GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps));
 	gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
 
-	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain));
+	gst_pad_set_chain_function(self->sinkpad,
+		GST_DEBUG_FUNCPTR(sbc_enc_chain));
 
 	self->subbands = SBC_ENC_DEFAULT_SUB_BANDS;
 	self->blocks = SBC_ENC_DEFAULT_BLOCKS;
@@ -578,5 +528,16 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass)
 	self->rate = SBC_ENC_DEFAULT_RATE;
 	self->channels = SBC_ENC_DEFAULT_CHANNELS;
 
+	self->frame_length = 0;
+	self->frame_duration = 0;
+
 	self->adapter = gst_adapter_new();
 }
+
+gboolean gst_sbc_enc_plugin_init (GstPlugin * plugin)
+{
+	return gst_element_register (plugin, "sbcenc",
+			GST_RANK_NONE, GST_TYPE_SBC_ENC);
+}
+
+
diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h
index d81428c..c7b2163 100644
--- a/audio/gstsbcenc.h
+++ b/audio/gstsbcenc.h
@@ -68,4 +68,6 @@ struct _GstSbcEncClass {
 
 GType gst_sbc_enc_get_type(void);
 
+gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin);
+
 G_END_DECLS
diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c
index bae7d62..49d0bb6 100644
--- a/audio/gstsbcparse.c
+++ b/audio/gstsbcparse.c
@@ -56,171 +56,83 @@ static GstStaticPadTemplate sbc_parse_src_factory =
 				"allocation = (string) { snr, loudness },"
 				"bitpool = (int) [ 2, 64 ]"));
 
-/* Creates a fixed caps from the caps given. */
-/* FIXME use gstsbcutil caps fixating function */
-static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
+static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps)
 {
-	GstCaps *result;
+	GstSbcParse *parse;
 	GstStructure *structure;
-	const GValue *value;
-	gboolean error = FALSE;
-	gint temp, rate, channels, blocks, subbands, bitpool;
-	const gchar* allocation = NULL;
-	const gchar* mode = NULL;
-	const gchar* error_message = NULL;
-	gchar* str;
-
-	str = gst_caps_to_string(caps);
-	GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str);
-	g_free(str);
+	gint rate, channels;
+
+	parse = GST_SBC_PARSE(GST_PAD_PARENT(pad));
 
 	structure = gst_caps_get_structure(caps, 0);
 
-	if (!gst_structure_has_field(structure, "rate")) {
-		error = TRUE;
-		error_message = "no rate.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "rate");
-		if (GST_VALUE_HOLDS_LIST(value))
-			temp = gst_sbc_select_rate_from_list(value);
-		else
-			temp = g_value_get_int(value);
-		rate = temp;
-	}
+	if (!gst_structure_get_int(structure, "rate", &rate))
+		return FALSE;
 
-	if (!gst_structure_has_field(structure, "channels")) {
-		error = TRUE;
-		error_message = "no channels.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "channels");
-		if (GST_VALUE_HOLDS_INT_RANGE(value))
-			temp = gst_sbc_select_channels_from_range(value);
-		else
-			temp = g_value_get_int(value);
-		channels = temp;
-	}
+	if (!gst_structure_get_int(structure, "channels", &channels))
+		return FALSE;
 
-	if (!gst_structure_has_field(structure, "blocks")) {
-		error = TRUE;
-		error_message = "no blocks.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "blocks");
-		if (GST_VALUE_HOLDS_LIST(value))
-			temp = gst_sbc_select_blocks_from_list(value);
-		else
-			temp = g_value_get_int(value);
-		blocks = temp;
-	}
+	if (!(parse->rate == 0 || rate == parse->rate))
+		return FALSE;
 
-	if (!gst_structure_has_field(structure, "subbands")) {
-		error = TRUE;
-		error_message = "no subbands.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "subbands");
-		if (GST_VALUE_HOLDS_LIST(value))
-			temp = gst_sbc_select_subbands_from_list(value);
-		else
-			temp = g_value_get_int(value);
-		subbands = temp;
-	}
+	if (!(parse->channels == 0 || channels == parse->channels))
+		return FALSE;
 
-	if (!gst_structure_has_field(structure, "bitpool")) {
-		error = TRUE;
-		error_message = "no bitpool";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "bitpool");
-		if (GST_VALUE_HOLDS_INT_RANGE(value))
-			temp = gst_sbc_select_bitpool_from_range(value);
-		else
-			temp = g_value_get_int(value);
-		bitpool = temp;
-	}
+	parse->rate = rate;
+	parse->channels = channels;
 
-	if (!gst_structure_has_field(structure, "allocation")) {
-		error = TRUE;
-		error_message = "no allocation.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "allocation");
-		if (GST_VALUE_HOLDS_LIST(value))
-			allocation = gst_sbc_get_allocation_from_list(value);
-		else
-			allocation = g_value_get_string(value);
-	}
+	return gst_sbc_util_fill_sbc_params(&parse->sbc, caps);
+}
 
-	if (!gst_structure_has_field(structure, "mode")) {
-		error = TRUE;
-		error_message = "no mode.";
-		goto error;
-	} else {
-		value = gst_structure_get_value(structure, "mode");
-		if (GST_VALUE_HOLDS_LIST(value))
-			mode = gst_sbc_get_mode_from_list(value);
-		else
-			mode = g_value_get_string(value);
-	}
+static GstCaps* sbc_parse_src_getcaps(GstPad *pad)
+{
+	GstCaps *caps;
+	const GstCaps *allowed_caps;
+	GstStructure *structure;
+	GValue *value;
+	GstSbcParse *parse = GST_SBC_PARSE(GST_PAD_PARENT(pad));
 
-error:
-	if (error) {
-		GST_ERROR_OBJECT (parse, "Invalid input caps: %s",
-				error_message);
-		return NULL;
-	}
+	allowed_caps = gst_pad_get_allowed_caps(pad);
+	if (allowed_caps == NULL)
+		allowed_caps = gst_pad_get_pad_template_caps(pad);
+	caps = gst_caps_copy(allowed_caps);
 
-	result = gst_caps_new_simple("audio/x-sbc",
-					"rate", G_TYPE_INT, rate,
-					"channels", G_TYPE_INT, channels,
-					"mode", G_TYPE_STRING, mode,
-					"blocks", G_TYPE_INT, blocks,
-					"subbands", G_TYPE_INT, subbands,
-					"allocation", G_TYPE_STRING, allocation,
-					"bitpool", G_TYPE_INT, bitpool,
-					NULL);
-	parse->sbc.rate = rate;
-	parse->sbc.channels = channels;
-	parse->sbc.blocks = blocks;
-	parse->sbc.subbands = subbands;
-	parse->sbc.bitpool = bitpool;
-	parse->sbc.joint = gst_sbc_get_mode_int(mode);
-	parse->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation);
-
-	return result;
+	value = g_new0(GValue, 1);
+
+	structure = gst_caps_get_structure(caps, 0);
+
+	if (parse->rate != 0)
+		gst_sbc_util_set_structure_int_param(structure, "rate",
+				parse->rate, value);
+	if (parse->channels != 0)
+		gst_sbc_util_set_structure_int_param(structure, "channels",
+				parse->channels, value);
+
+	g_free(value);
+
+	return caps;
 }
 
-static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps)
+static gboolean sbc_parse_src_acceptcaps(GstPad *pad, GstCaps *caps)
 {
+	GstStructure *structure;
 	GstSbcParse *parse;
-	GstCaps *inter, *other, *srccaps;
+	gint rate, channels;
 
 	parse = GST_SBC_PARSE(GST_PAD_PARENT(pad));
 
-	other = gst_pad_peer_get_caps(parse->srcpad);
-	if (other == NULL)
-		other = gst_caps_new_any();
+	structure = gst_caps_get_structure(caps, 0);
 
-	inter = gst_caps_intersect(caps, other);
-	if (gst_caps_is_empty(inter)) {
-		gst_caps_unref(inter);
+	if (!gst_structure_get_int(structure, "rate", &rate))
 		return FALSE;
-	}
-	srccaps = sbc_parse_select_caps(parse, inter);
-	if (srccaps == NULL) {
-		gst_caps_unref(inter);
+	if (!gst_structure_get_int(structure, "channels", &channels))
 		return FALSE;
-	}
 
-	gst_pad_set_caps(parse->srcpad, srccaps);
+	if ((parse->rate == 0 || parse->rate == rate)
+		&& (parse->channels == 0 || parse->channels == channels))
+		return TRUE;
 
-	gst_caps_unref(inter);
-	gst_caps_unref(other);
-	gst_caps_unref(srccaps);
-
-	return TRUE;
+	return FALSE;
 }
 
 static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer)
@@ -234,11 +146,13 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer)
 	timestamp = GST_BUFFER_TIMESTAMP(buffer);
 
 	if (parse->buffer) {
-		GstBuffer *temp = buffer;
+		GstBuffer *temp;
+		temp = buffer;
 		buffer = gst_buffer_span(parse->buffer, 0, buffer,
-			GST_BUFFER_SIZE(parse->buffer) + GST_BUFFER_SIZE(buffer));
-		gst_buffer_unref(temp);
+			GST_BUFFER_SIZE(parse->buffer)
+			+ GST_BUFFER_SIZE(buffer));
 		gst_buffer_unref(parse->buffer);
+		gst_buffer_unref(temp);
 		parse->buffer = NULL;
 	}
 
@@ -300,11 +214,13 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element,
 
 	case GST_STATE_CHANGE_PAUSED_TO_READY:
 		GST_DEBUG("Finish subband codec");
+
 		if (parse->buffer) {
 			gst_buffer_unref(parse->buffer);
 			parse->buffer = NULL;
 		}
 		sbc_finish(&parse->sbc);
+
 		break;
 
 	default:
@@ -341,12 +257,27 @@ static void gst_sbc_parse_class_init(GstSbcParseClass *klass)
 
 static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass)
 {
-	self->sinkpad = gst_pad_new_from_static_template(&sbc_parse_sink_factory, "sink");
-	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain));
+	self->sinkpad = gst_pad_new_from_static_template(
+		&sbc_parse_sink_factory, "sink");
+	gst_pad_set_chain_function(self->sinkpad,
+		GST_DEBUG_FUNCPTR(sbc_parse_chain));
 	gst_pad_set_setcaps_function (self->sinkpad,
 		GST_DEBUG_FUNCPTR (sbc_parse_sink_setcaps));
 	gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
 
-	self->srcpad = gst_pad_new_from_static_template(&sbc_parse_src_factory, "src");
+	self->srcpad = gst_pad_new_from_static_template(
+		&sbc_parse_src_factory, "src");
+	gst_pad_set_getcaps_function (self->srcpad,
+		GST_DEBUG_FUNCPTR (sbc_parse_src_getcaps));
+	gst_pad_set_acceptcaps_function (self->srcpad,
+		GST_DEBUG_FUNCPTR (sbc_parse_src_acceptcaps));
+	/* FIXME get encoding parameters on set caps */
 	gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
 }
+
+gboolean gst_sbc_parse_plugin_init (GstPlugin * plugin)
+{
+	return gst_element_register (plugin, "sbcparse",
+			GST_RANK_NONE, GST_TYPE_SBC_PARSE);
+}
+
diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h
index ceaf219..eb9ca44 100644
--- a/audio/gstsbcparse.h
+++ b/audio/gstsbcparse.h
@@ -50,6 +50,9 @@ struct _GstSbcParse {
 	GstBuffer *buffer;
 
 	sbc_t sbc;
+
+	gint channels;
+	gint rate;
 };
 
 struct _GstSbcParseClass {
@@ -58,4 +61,6 @@ struct _GstSbcParseClass {
 
 GType gst_sbc_parse_get_type(void);
 
+gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin);
+
 G_END_DECLS
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
index f2351e6..d791a8d 100644
--- a/audio/gstsbcutil.c
+++ b/audio/gstsbcutil.c
@@ -26,6 +26,7 @@
 #endif
 
 #include "ipc.h"
+#include <math.h>
 #include "gstsbcutil.h"
 
 /*
@@ -306,4 +307,103 @@ error:
 	return result;
 }
 
+/**
+ * Sets the int field_value to the  param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+			const gchar* field, gint field_value,
+			GValue *value)
+{
+	value = g_value_init(value, G_TYPE_INT);
+	g_value_set_int(value, field_value);
+	gst_structure_set_value(structure, field, value);
+	g_value_unset(value);
+}
+
+/**
+ * Sets the string field_value to the  param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+			const gchar* field, const gchar* field_value,
+			GValue *value)
+{
+	value = g_value_init(value, G_TYPE_STRING);
+	g_value_set_string(value, field_value);
+	gst_structure_set_value(structure, field, value);
+	g_value_unset(value);
+}
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps)
+{
+	GstStructure *structure;
+	gint rate, channels, subbands, blocks, bitpool;
+	const gchar* mode;
+	const gchar* allocation;
+
+	g_assert(gst_caps_is_fixed(caps));
+
+	structure = gst_caps_get_structure(caps, 0);
+
+	if (!gst_structure_get_int(structure, "rate", &rate))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "channels", &channels))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "subbands", &subbands))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "blocks", &blocks))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+		return FALSE;
+
+	if (!(mode = gst_structure_get_string(structure, "mode")))
+		return FALSE;
+	if (!(allocation = gst_structure_get_string(structure, "allocation")))
+		return FALSE;
+
+	sbc->rate = rate;
+	sbc->channels = channels;
+	sbc->blocks = blocks;
+	sbc->subbands = subbands;
+	sbc->bitpool = bitpool;
+	sbc->joint = gst_sbc_get_mode_int(mode);
+	sbc->allocation = gst_sbc_get_allocation_mode_int(allocation);
+
+	return TRUE;
+}
+
+gint gst_sbc_util_calc_frame_len(gint subbands, gint channels,
+                gint blocks, gint bitpool, gint channel_mode)
+{
+        gint len;
+        gint join;
+        len = 4 + (4 * subbands * channels)/8;
+
+        if (channel_mode == BT_A2DP_CHANNEL_MODE_MONO ||
+                channel_mode == BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+                len += ((blocks * channels * bitpool)+7) / 8;
+        else {
+                join = channel_mode == BT_A2DP_CHANNEL_MODE_JOINT_STEREO
+			? 1 : 0;
+                len += ((join * subbands + blocks * bitpool)+7)/8;
+        }
+
+        return len;
+}
+
+gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands,
+                gint blocks)
+{
+	return (((frame_len * 8 * rate / subbands) / blocks) / 1000);
+}
+
+gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands)
+{
+	gint64 res = 1000000;
+	return res * blocks * subbands / rate;
+}
+
 
diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h
index 4581abf..a67bf1b 100644
--- a/audio/gstsbcutil.h
+++ b/audio/gstsbcutil.h
@@ -49,3 +49,22 @@ const gchar *gst_sbc_get_mode_string(int joint);
 GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels);
 
 GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message);
+
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+			const gchar* field, gint field_value,
+			GValue *value);
+
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+			const gchar* field, const gchar* field_value,
+			GValue *value);
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps);
+
+gint gst_sbc_util_calc_frame_len(gint subbands, gint channels,
+                gint blocks, gint bitpool, gint channel_mode);
+
+gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands,
+		gint blocks);
+
+gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands);
+
-- 
1.5.3.7
0003-Add-missing-files.patch (text/x-diff, 42.8 KB)
From 227dda4b0863a48826a1d42adeecebfe87134940 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Wed, 12 Dec 2007 12:08:36 -0300
Subject: [PATCH] Add missing files.

---
 audio/gsta2dpsendersink.c | 1013 +++++++++++++++++++++++++++++++++++++++++++++
 audio/gsta2dpsendersink.h |   90 ++++
 audio/gstrtpsbcpay.c      |  337 +++++++++++++++
 audio/gstrtpsbcpay.h      |   65 +++
 4 files changed, 1505 insertions(+), 0 deletions(-)
 create mode 100644 audio/gsta2dpsendersink.c
 create mode 100644 audio/gsta2dpsendersink.h
 create mode 100644 audio/gstrtpsbcpay.c
 create mode 100644 audio/gstrtpsbcpay.h

diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c
new file mode 100644
index 0000000..cfb67b8
--- /dev/null
+++ b/audio/gsta2dpsendersink.c
@@ -0,0 +1,1013 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2007  Marcel Holtmann <[email protected]>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; 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 <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "ipc.h"
+#include "rtp.h"
+#include "gstsbcutil.h"
+
+#include "gsta2dpsendersink.h"
+
+GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug);
+#define GST_CAT_DEFAULT a2dp_sender_sink_debug
+
+#define BUFFER_SIZE 2048
+#define TEMPLATE_MAX_BITPOOL 64
+
+#define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START {	\
+		g_mutex_lock (s->sink_lock);		\
+	} G_STMT_END
+
+#define GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(s) G_STMT_START {	\
+		g_mutex_unlock (s->sink_lock);		\
+	} G_STMT_END
+
+
+struct bluetooth_data {
+	struct bt_getcapabilities_rsp caps; /* Bluetooth device caps */
+	guint link_mtu;
+
+	gchar buffer[BUFFER_SIZE];	/* Codec transfer buffer */
+};
+
+#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
+#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0)
+
+enum {
+	PROP_0,
+	PROP_DEVICE,
+};
+
+GST_BOILERPLATE(GstA2dpSenderSink, gst_a2dp_sender_sink, GstBaseSink,
+			GST_TYPE_BASE_SINK);
+
+static const GstElementDetails a2dp_sender_sink_details =
+	GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
+				"Sink/Audio",
+				"Plays audio to an A2DP device",
+				"Marcel Holtmann <[email protected]>");
+
+static GstStaticPadTemplate a2dp_sender_sink_factory =
+	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+		GST_STATIC_CAPS("application/x-rtp, "
+				"media = (string) \"audio\", "
+				"encoding-name = (string) \"SBC\""
+				));
+
+static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self,
+					const bt_audio_msg_header_t *msg);
+static GIOError gst_a2dp_sender_sink_audioservice_expect(
+				GstA2dpSenderSink *self,
+				bt_audio_msg_header_t *outmsg,
+				int expected_type);
+
+
+static void gst_a2dp_sender_sink_base_init(gpointer g_class)
+{
+	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+	gst_element_class_add_pad_template(element_class,
+		gst_static_pad_template_get(&a2dp_sender_sink_factory));
+
+	gst_element_class_set_details(element_class, &a2dp_sender_sink_details);
+}
+
+static gboolean gst_a2dp_sender_sink_stop(GstBaseSink *basesink)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+
+	GST_INFO_OBJECT(self, "stop");
+
+	if (self->watch_id != 0) {
+		g_source_remove(self->watch_id);
+		self->watch_id = 0;
+	}
+
+	if (self->server) {
+		bt_audio_service_close(g_io_channel_unix_get_fd(self->server));
+		g_io_channel_unref(self->server);
+		self->server = NULL;
+	}
+
+	if (self->stream) {
+		g_io_channel_flush(self->stream, NULL);
+		g_io_channel_close(self->stream);
+		g_io_channel_unref(self->stream);
+		self->stream = NULL;
+	}
+
+	if (self->data) {
+		g_free(self->data);
+		self->data = NULL;
+	}
+
+	if (self->stream_caps) {
+		gst_caps_unref(self->stream_caps);
+		self->stream_caps = NULL;
+	}
+
+	if (self->dev_caps) {
+		gst_caps_unref(self->dev_caps);
+		self->dev_caps = NULL;
+	}
+
+	return TRUE;
+}
+
+static void gst_a2dp_sender_sink_finalize(GObject *object)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(object);
+
+	if (self->data)
+		gst_a2dp_sender_sink_stop(GST_BASE_SINK(self));
+
+	if (self->device)
+		g_free(self->device);
+
+	g_mutex_free(self->sink_lock);
+
+	G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void gst_a2dp_sender_sink_set_property(GObject *object, guint prop_id,
+					const GValue *value, GParamSpec *pspec)
+{
+	GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object);
+
+	switch (prop_id) {
+	case PROP_DEVICE:
+		if (sink->device)
+			g_free(sink->device);
+		sink->device = g_value_dup_string(value);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void gst_a2dp_sender_sink_get_property(GObject *object, guint prop_id,
+					GValue *value, GParamSpec *pspec)
+{
+	GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object);
+
+	switch (prop_id) {
+	case PROP_DEVICE:
+		g_value_set_string(value, sink->device);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static gint gst_a2dp_sender_sink_bluetooth_recvmsg_fd(GstA2dpSenderSink *sink)
+{
+	int err, ret;
+
+	ret = bt_audio_service_get_data_fd(
+			g_io_channel_unix_get_fd(sink->server));
+
+	if (ret < 0) {
+		err = errno;
+		GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
+				strerror(err), err);
+		return -err;
+	}
+
+	sink->stream = g_io_channel_unix_new(ret);
+	GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);
+
+	return 0;
+}
+
+static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink,
+					GstCaps *caps,
+					sbc_capabilities_t *pkt)
+{
+	sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities;
+	const GValue *value = NULL;
+	const char *pref, *name;
+	gint rate, subbands, blocks;
+	GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+	name = gst_structure_get_name(structure);
+	/* FIXME only sbc supported here, should suport mp3 */
+	if (!(IS_SBC(name))) {
+		GST_ERROR_OBJECT(sink, "Unsupported format %s", name);
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "rate");
+	rate = g_value_get_int(value);
+	if (rate == 44100)
+		cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100;
+	else if (rate == 48000)
+		cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000;
+	else if (rate == 32000)
+		cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000;
+	else if (rate == 16000)
+		cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000;
+	else {
+		GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "mode");
+	pref = g_value_get_string(value);
+	if (strcmp(pref, "auto") == 0)
+		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;
+	else if (strcmp(pref, "mono") == 0)
+		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+	else if (strcmp(pref, "dual") == 0)
+		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+	else if (strcmp(pref, "stereo") == 0)
+		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+	else if (strcmp(pref, "joint") == 0)
+		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+	else {
+		GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "allocation");
+	pref = g_value_get_string(value);
+	if (strcmp(pref, "auto") == 0)
+		cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO;
+	else if (strcmp(pref, "loudness") == 0)
+		cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+	else if (strcmp(pref, "snr") == 0)
+		cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
+	else {
+		GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "subbands");
+	subbands = g_value_get_int(value);
+	if (subbands == 8)
+		cfg->subbands = BT_A2DP_SUBBANDS_8;
+	else if (subbands == 4)
+		cfg->subbands = BT_A2DP_SUBBANDS_4;
+	else {
+		GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands);
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "blocks");
+	blocks = g_value_get_int(value);
+	if (blocks == 16)
+		cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
+	else if (blocks == 12)
+		cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
+	else if (blocks == 8)
+		cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
+	else if (blocks == 4)
+		cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
+	else {
+		GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks);
+		return FALSE;
+	}
+
+	value = gst_structure_get_value(structure, "bitpool");
+	cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
+
+	memcpy(pkt, cfg, sizeof(*pkt));
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sender_sink_conf_recv_stream_fd(
+					GstA2dpSenderSink *self)
+{
+	struct bluetooth_data *data = self->data;
+	gint ret;
+	GIOError err;
+	GError *gerr = NULL;
+	GIOStatus status;
+	GIOFlags flags;
+	gsize read;
+
+	ret = gst_a2dp_sender_sink_bluetooth_recvmsg_fd(self);
+	if (ret < 0)
+		return FALSE;
+
+	if (!self->stream) {
+		GST_ERROR_OBJECT(self, "Error while configuring device: "
+				"could not acquire audio socket");
+		return FALSE;
+	}
+
+	/* set stream socket to nonblock */
+	GST_LOG_OBJECT(self, "setting stream socket to nonblock");
+	flags = g_io_channel_get_flags(self->stream);
+	flags |= G_IO_FLAG_NONBLOCK;
+	status = g_io_channel_set_flags(self->stream, flags, &gerr);
+	if (status != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			GST_WARNING_OBJECT(self, "Error while "
+				"setting server socket to nonblock: "
+				"%s", gerr->message);
+		else
+			GST_WARNING_OBJECT(self, "Error while "
+					"setting server "
+					"socket to nonblock");
+	}
+
+	/* It is possible there is some outstanding
+	data in the pipe - we have to empty it */
+	GST_LOG_OBJECT(self, "emptying stream pipe");
+	while (1) {
+		err = g_io_channel_read(self->stream, data->buffer,
+					(gsize) data->link_mtu,
+					&read);
+		if (err != G_IO_ERROR_NONE || read <= 0)
+			break;
+	}
+
+	/* set stream socket to block */
+	GST_LOG_OBJECT(self, "setting stream socket to block");
+	flags = g_io_channel_get_flags(self->stream);
+	flags &= ~G_IO_FLAG_NONBLOCK;
+	status = g_io_channel_set_flags(self->stream, flags, &gerr);
+	if (status != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			GST_WARNING_OBJECT(self, "Error while "
+				"setting server socket to block:"
+				"%s", gerr->message);
+		else
+			GST_WARNING_OBJECT(self, "Error while "
+				"setting server "
+				"socket to block");
+	}
+
+	memset(data->buffer, 0, sizeof(data->buffer));
+
+	return TRUE;
+}
+
+static gboolean server_callback(GIOChannel *chan,
+					GIOCondition cond, gpointer data)
+{
+	GstA2dpSenderSink *sink;
+
+	if (cond & G_IO_HUP || cond & G_IO_NVAL)
+		return FALSE;
+	else if (cond & G_IO_ERR) {
+		sink = GST_A2DP_SENDER_SINK(data);
+		GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR");
+	}
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
+{
+	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
+	GstStructure *structure;
+	GValue *value;
+	GValue *list;
+	gchar *tmp;
+	gboolean mono, stereo;
+
+	GST_LOG_OBJECT(self, "updating device caps");
+
+	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_AUTO) {
+		g_value_set_static_string(value, "joint");
+		gst_value_list_prepend_value(list, value);
+		g_value_set_static_string(value, "stereo");
+		gst_value_list_prepend_value(list, value);
+		g_value_set_static_string(value, "mono");
+		gst_value_list_prepend_value(list, value);
+		g_value_set_static_string(value, "dual");
+		gst_value_list_prepend_value(list, value);
+	} else {
+		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_AUTO) {
+		g_value_set_static_string(value, "loudness");
+		gst_value_list_prepend_value(list, value);
+		g_value_set_static_string(value, "snr");
+		gst_value_list_prepend_value(list, value);
+	} else {
+		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_A2DP_SAMPLING_FREQ_48000) {
+		g_value_set_int(value, 48000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) {
+		g_value_set_int(value, 44100);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) {
+		g_value_set_int(value, 32000);
+		gst_value_list_prepend_value(list, value);
+	}
+	if (sbc->frequency & BT_A2DP_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 */
+	if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) {
+		g_value_init(value, GST_TYPE_INT_RANGE);
+		gst_value_set_int_range(value, 1, 2);
+	} else {
+		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);
+
+	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, "Device capabilities: %s", tmp);
+	g_free(tmp);
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self)
+{
+	gchar *buf[BT_AUDIO_IPC_PACKET_SIZE];
+	struct bt_getcapabilities_req *req = (void *) buf;
+	struct bt_getcapabilities_rsp *rsp = (void *) buf;
+	GIOError io_error;
+
+	memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+
+	req->h.msg_type = BT_GETCAPABILITIES_REQ;
+	if (self->device == NULL)
+		return FALSE;
+	strncpy(req->device, self->device, 18);
+
+	io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while asking device caps");
+	}
+
+	io_error = gst_a2dp_sender_sink_audioservice_expect(self,
+			&rsp->rsp_h.msg_h, BT_GETCAPABILITIES_RSP);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while getting device caps");
+		return FALSE;
+	}
+
+	if (rsp->rsp_h.posix_errno != 0) {
+		GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)",
+					strerror(rsp->rsp_h.posix_errno),
+					rsp->rsp_h.posix_errno);
+		return FALSE;
+	}
+
+	memcpy(&self->data->caps, rsp, sizeof(*rsp));
+	if (!gst_a2dp_sender_sink_update_caps(self)) {
+		GST_WARNING_OBJECT(self, "failed to update capabilities");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+	gint sk;
+	gint err;
+
+	GST_INFO_OBJECT(self, "start");
+
+	self->watch_id = 0;
+
+	sk = bt_audio_service_open();
+	if (sk <= 0) {
+		err = errno;
+		GST_ERROR_OBJECT(self, "Cannot open connection to bt "
+			"audio service: %s %d", strerror(err), err);
+		goto failed;
+	}
+
+	self->server = g_io_channel_unix_new(sk);
+	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);
+	memset(self->data, 0, sizeof(struct bluetooth_data));
+
+	self->stream = NULL;
+	self->stream_caps = NULL;
+
+	if (!gst_a2dp_sender_sink_get_capabilities(self))
+		goto failed;
+
+	return TRUE;
+
+failed:
+	bt_audio_service_close(sk);
+	return FALSE;
+}
+
+static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self)
+{
+	gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
+	struct bt_streamstart_req *req = (void *) buf;
+	struct bt_streamstart_rsp *rsp = (void *) buf;
+	struct bt_streamfd_ind *ind = (void*) buf;
+	GIOError io_error;
+
+	GST_DEBUG_OBJECT(self, "stream start");
+
+	memset (req, 0, sizeof(buf));
+	req->h.msg_type = BT_STREAMSTART_REQ;
+
+	io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error ocurred while sending "
+					"start packet");
+		return FALSE;
+	}
+
+	GST_DEBUG_OBJECT(self, "stream start packet sent");
+
+	io_error = gst_a2dp_sender_sink_audioservice_expect(self,
+			&rsp->rsp_h.msg_h, BT_STREAMSTART_RSP);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while stream "
+			"start confirmation");
+		return FALSE;
+	}
+
+	if (rsp->rsp_h.posix_errno != 0) {
+		GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)",
+					strerror(rsp->rsp_h.posix_errno),
+					rsp->rsp_h.posix_errno);
+		return FALSE;
+	}
+
+	GST_DEBUG_OBJECT(self, "stream started");
+
+	io_error = gst_a2dp_sender_sink_audioservice_expect(self, &ind->h,
+			BT_STREAMFD_IND);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while receiving "
+			"stream filedescriptor");
+		return FALSE;
+	}
+
+	if (!gst_a2dp_sender_sink_conf_recv_stream_fd(self))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self,
+			GstCaps *caps)
+{
+	gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
+	struct bt_setconfiguration_req *req = (void *) buf;
+	struct bt_setconfiguration_rsp *rsp = (void *) buf;
+	gboolean ret;
+	GIOError io_error;
+	gchar *temp;
+
+	temp = gst_caps_to_string(caps);
+	GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
+	g_free(temp);
+
+	memset (req, 0, sizeof(buf));
+	req->h.msg_type = BT_SETCONFIGURATION_REQ;
+	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+	strncpy(req->device, self->device, 18);
+	ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps,
+			&req->sbc_capabilities);
+	if (!ret) {
+		GST_ERROR_OBJECT(self, "Couldn't parse caps "
+				"to packet configuration");
+		return FALSE;
+	}
+
+	io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error ocurred while sending "
+					"configurarion packet");
+		return FALSE;
+	}
+
+	GST_DEBUG_OBJECT(self, "configuration packet sent");
+
+	io_error = gst_a2dp_sender_sink_audioservice_expect(self,
+			&rsp->rsp_h.msg_h, BT_SETCONFIGURATION_RSP);
+	if (io_error != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while receiving device "
+					"confirmation");
+		return FALSE;
+	}
+
+	if (rsp->rsp_h.posix_errno != 0) {
+		GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : "
+					"%s(%d)",
+					strerror(rsp->rsp_h.posix_errno),
+					rsp->rsp_h.posix_errno);
+		return FALSE;
+	}
+
+	self->data->link_mtu = rsp->link_mtu;
+	GST_DEBUG_OBJECT(self, "configuration set");
+
+	return TRUE;
+}
+
+static GstFlowReturn gst_a2dp_sender_sink_preroll(GstBaseSink *basesink,
+					GstBuffer *buffer)
+{
+	GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(basesink);
+	gboolean ret;
+
+	GST_A2DP_SENDER_SINK_MUTEX_LOCK(sink);
+
+	ret = gst_a2dp_sender_sink_stream_start(sink);
+
+	GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(sink);
+
+	if (!ret)
+		return GST_FLOW_ERROR;
+
+	return GST_FLOW_OK;
+}
+
+static GstFlowReturn gst_a2dp_sender_sink_render(GstBaseSink *basesink,
+					GstBuffer *buffer)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+	gsize ret;
+	GIOError err;
+
+	err = g_io_channel_write(self->stream, (gchar*)GST_BUFFER_DATA(buffer),
+			 (gsize)(GST_BUFFER_SIZE(buffer)), &ret);
+
+	if (err != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error while writting to socket: %d %s",
+				errno, strerror(errno));
+		return GST_FLOW_ERROR;
+	}
+
+	return GST_FLOW_OK;
+}
+
+static gboolean gst_a2dp_sender_sink_unlock(GstBaseSink *basesink)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+
+	if (self->stream != NULL)
+		g_io_channel_flush (self->stream, NULL);
+
+	return TRUE;
+}
+
+static GstFlowReturn gst_a2dp_sender_sink_buffer_alloc(GstBaseSink *basesink,
+				guint64 offset, guint size, GstCaps* caps,
+				GstBuffer **buf)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+
+	*buf = gst_buffer_new_and_alloc(size);
+	if (!(*buf)) {
+		GST_ERROR_OBJECT(self, "buffer allocation failed");
+		return GST_FLOW_ERROR;
+	}
+
+	gst_buffer_set_caps(*buf, caps);
+
+	GST_BUFFER_OFFSET(*buf) = offset;
+
+	return GST_FLOW_OK;
+}
+
+static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS(klass);
+	GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	object_class->finalize = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_finalize);
+	object_class->set_property = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_set_property);
+	object_class->get_property = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_get_property);
+
+	basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_start);
+	basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_stop);
+	basesink_class->render = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_render);
+	basesink_class->preroll = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_preroll);
+	basesink_class->unlock = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_unlock);
+
+	basesink_class->buffer_alloc =
+		GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc);
+
+	g_object_class_install_property(object_class, PROP_DEVICE,
+					g_param_spec_string("device", "Device",
+					"Bluetooth remote device address",
+					NULL, G_PARAM_READWRITE));
+
+	GST_DEBUG_CATEGORY_INIT(a2dp_sender_sink_debug, "a2dpsendersink", 0,
+				"A2DP sink element");
+}
+
+static void gst_a2dp_sender_sink_init(GstA2dpSenderSink *self,
+			GstA2dpSenderSinkClass *klass)
+{
+	self->device = NULL;
+	self->data = NULL;
+
+	self->stream = NULL;
+
+	self->dev_caps = NULL;
+
+	self->sink_lock = g_mutex_new();
+}
+
+static GIOError gst_a2dp_sender_sink_audioservice_send(
+					GstA2dpSenderSink *self,
+					const bt_audio_msg_header_t *msg)
+{
+	GIOError error;
+	gsize written;
+
+	error = g_io_channel_write(self->server, (const gchar*) msg,
+			BT_AUDIO_IPC_PACKET_SIZE, &written);
+	if (error != G_IO_ERROR_NONE)
+		GST_ERROR_OBJECT(self, "Error sending data to audio service:"
+			" %s(%d)", strerror(errno), errno);
+
+	return error;
+}
+
+static GIOError gst_a2dp_sender_sink_audioservice_recv(
+					GstA2dpSenderSink *self,
+					bt_audio_msg_header_t *inmsg)
+{
+	GIOError status;
+	gsize bytes_read;
+	const char *type;
+
+	status = g_io_channel_read(self->server, (gchar*) inmsg,
+			BT_AUDIO_IPC_PACKET_SIZE, &bytes_read);
+	if (status != G_IO_ERROR_NONE) {
+		GST_ERROR_OBJECT(self, "Error receiving data from "
+				"audio service");
+		return status;
+	}
+
+	type = bt_audio_strmsg(inmsg->msg_type);
+	if (!type) {
+		status = G_IO_ERROR_INVAL;
+		GST_ERROR_OBJECT(self, "Bogus message type %d "
+				"received from audio service",
+				inmsg->msg_type);
+	}
+
+	return status;
+}
+
+static GIOError gst_a2dp_sender_sink_audioservice_expect(
+			GstA2dpSenderSink *self, bt_audio_msg_header_t *outmsg,
+			int expected_type)
+{
+	GIOError status;
+
+	status = gst_a2dp_sender_sink_audioservice_recv(self, outmsg);
+	if (status != G_IO_ERROR_NONE)
+		return status;
+
+	if (outmsg->msg_type != expected_type)
+		status = G_IO_ERROR_INVAL;
+
+	return status;
+}
+
+gboolean gst_a2dp_sender_sink_plugin_init (GstPlugin * plugin)
+{
+	return gst_element_register (plugin, "a2dpsendersink",
+			GST_RANK_NONE, GST_TYPE_A2DP_SENDER_SINK);
+}
+
+
+/* public functions */
+GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink)
+{
+	if (sink->dev_caps == NULL)
+		return NULL;
+
+	return gst_caps_copy(sink->dev_caps);
+}
+
+gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *self,
+			GstCaps *caps)
+{
+	gboolean ret;
+
+	GST_DEBUG_OBJECT(self, "setting device caps");
+	GST_A2DP_SENDER_SINK_MUTEX_LOCK(self);
+	ret = gst_a2dp_sender_sink_configure(self, caps);
+
+	if (self->stream_caps)
+		gst_caps_unref(self->stream_caps);
+	self->stream_caps = gst_caps_ref(caps);
+
+	GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(self);
+
+	return ret;
+}
+
+guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink)
+{
+	return sink->data->link_mtu;
+}
+
+void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *self, const gchar* dev)
+{
+	if (self->device != NULL)
+		g_free(self->device);
+
+	GST_LOG_OBJECT(self, "Setting device: %s", dev);
+	self->device = g_strdup(dev);
+}
+
+gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self)
+{
+	return g_strdup(self->device);
+}
+
diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h
new file mode 100644
index 0000000..863ef6c
--- /dev/null
+++ b/audio/gsta2dpsendersink.h
@@ -0,0 +1,90 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2007  Marcel Holtmann <[email protected]>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GST_A2DP_SENDER_SINK_H
+#define __GST_A2DP_SENDER_SINK_H
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_A2DP_SENDER_SINK \
+	(gst_a2dp_sender_sink_get_type())
+#define GST_A2DP_SENDER_SINK(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SENDER_SINK,\
+		GstA2dpSenderSink))
+#define GST_A2DP_SENDER_SINK_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SENDER_SINK,\
+		GstA2dpSenderSinkClass))
+#define GST_IS_A2DP_SENDER_SINK(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SENDER_SINK))
+#define GST_IS_A2DP_SENDER_SINK_CLASS(obj) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SENDER_SINK))
+
+typedef struct _GstA2dpSenderSink GstA2dpSenderSink;
+typedef struct _GstA2dpSenderSinkClass GstA2dpSenderSinkClass;
+
+struct bluetooth_data;
+
+struct _GstA2dpSenderSink {
+	GstBaseSink sink;
+
+	gchar *device;
+	GIOChannel *stream;
+
+	struct bluetooth_data *data;
+	GIOChannel *server;
+
+	/* stream connection data */
+	GstCaps *stream_caps;
+
+	GstCaps *dev_caps;
+
+	GMutex *sink_lock;
+
+	guint watch_id;
+};
+
+struct _GstA2dpSenderSinkClass {
+	GstBaseSinkClass parent_class;
+};
+
+GType gst_a2dp_sender_sink_get_type(void);
+
+GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink);
+gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *sink,
+			GstCaps *caps);
+
+guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink);
+
+void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *sink,
+		const gchar* device);
+
+gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink);
+
+gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
+
+#endif /* __GST_A2DP_SENDER_SINK_H */
diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c
new file mode 100644
index 0000000..68aa28a
--- /dev/null
+++ b/audio/gstrtpsbcpay.c
@@ -0,0 +1,337 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2007  Marcel Holtmann <[email protected]>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; 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 "gstrtpsbcpay.h"
+#include <math.h>
+#include <string.h>
+
+#define RTP_SBC_PAYLOAD_HEADER_SIZE 1
+#define DEFAULT_MIN_FRAMES 0
+#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+        guint8 frame_count:4;
+        guint8 rfa0:1;
+        guint8 is_last_fragment:1;
+        guint8 is_first_fragment:1;
+        guint8 is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+        guint8 is_fragmented:1;
+        guint8 is_first_fragment:1;
+        guint8 is_last_fragment:1;
+        guint8 rfa0:1;
+        guint8 frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+enum {
+	PROP_0,
+	PROP_MIN_FRAMES
+};
+
+GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug);
+#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug
+
+GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload,
+		GST_TYPE_BASE_RTP_PAYLOAD);
+
+static const GstElementDetails gst_rtp_sbc_pay_details =
+	GST_ELEMENT_DETAILS("RTP packet payloader",
+				"Codec/Payloader/Network",
+				"Payload SBC audio as RTP packets",
+				"Thiago Sousa Santos "
+				"<[email protected]>");
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory =
+	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+		GST_STATIC_CAPS("audio/x-sbc, " /* FIXME remove those caps? */
+				"rate = (int) { 16000, 32000, 44100, 48000 }, "
+				"channels = (int) [ 1, 2 ], "
+				"mode = (string) { mono, dual, stereo, joint }, "
+				"blocks = (int) { 4, 8, 12, 16 }, "
+				"subbands = (int) { 4, 8 }, "
+				"allocation = (string) { snr, loudness },"
+				"bitpool = (int) [ 2, 64 ]; ")
+	);
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory =
+	GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+		GST_STATIC_CAPS("application/x-rtp") /* FIXME put things here */
+	);
+
+static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id,
+                        const GValue * value, GParamSpec * pspec);
+static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id,
+                        GValue * value, GParamSpec * pspec);
+
+static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels,
+		gint blocks, gint bitpool, const gchar* channel_mode)
+{
+	gint len;
+	gint join;
+
+	len = 4 + (4 * subbands * channels)/8;
+
+	if (strcmp(channel_mode, "mono") == 0 ||
+		strcmp(channel_mode, "dual") == 0)
+		len += ((blocks * channels * bitpool)+7) / 8;
+	else {
+		join = strcmp(channel_mode, "joint") == 0 ? 1 : 0;
+		len += ((join * subbands + blocks * bitpool)+7)/8;
+	}
+
+	return len;
+}
+
+static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload,
+			GstCaps *caps)
+{
+	GstRtpSBCPay *sbcpay;
+	gint rate, subbands, channels, blocks, bitpool;
+	gint frame_len;
+	const gchar* channel_mode;
+	GstStructure *structure;
+
+	sbcpay = GST_RTP_SBC_PAY(payload);
+
+	structure = gst_caps_get_structure(caps, 0);
+	if (!gst_structure_get_int(structure, "rate", &rate))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "channels", &channels))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "blocks", &blocks))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+		return FALSE;
+	if (!gst_structure_get_int(structure, "subbands", &subbands))
+		return FALSE;
+
+	channel_mode = gst_structure_get_string(structure, "mode");
+	if (!channel_mode)
+		return FALSE;
+
+	frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks,
+				bitpool, channel_mode);
+
+	sbcpay->frame_length = frame_len;
+
+	gst_basertppayload_set_options (payload, "audio", FALSE, "SBC", rate);
+
+	GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len);
+
+	return gst_basertppayload_set_outcaps (payload, NULL);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay)
+{
+	guint available;
+	guint max_payload;
+	GstBuffer* outbuf;
+	guint8 *payload_data;
+	guint8 *data;
+	struct rtp_payload *payload;
+
+	if (sbcpay->frame_length == 0) {
+		GST_ERROR_OBJECT(sbcpay, "Frame length is 0");
+		return GST_FLOW_ERROR;
+	}
+
+	available = gst_adapter_available(sbcpay->adapter);
+
+	max_payload = gst_rtp_buffer_calc_payload_len(
+		GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE,
+		0, 0);
+
+	max_payload = MIN(max_payload, available);
+
+	outbuf = gst_rtp_buffer_new_allocate(max_payload +
+			RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0);
+
+	gst_rtp_buffer_set_payload_type(outbuf,
+			GST_BASE_RTP_PAYLOAD_PT(sbcpay));
+
+	data = gst_adapter_take(sbcpay->adapter, max_payload);
+	payload_data = gst_rtp_buffer_get_payload(outbuf);
+
+	payload = (struct rtp_payload*) payload_data;
+	memset(payload, 0, sizeof(struct rtp_payload));
+	payload->frame_count = max_payload / sbcpay->frame_length;
+
+	memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, max_payload);
+	g_free(data);
+
+	/* FIXME - timestamp it! */
+	GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", max_payload);
+
+	return gst_basertppayload_push (GST_BASE_RTP_PAYLOAD(sbcpay), outbuf);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload,
+			GstBuffer *buffer)
+{
+	GstRtpSBCPay *sbcpay;
+	guint available;
+
+	sbcpay = GST_RTP_SBC_PAY(payload);
+	gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer));
+
+	available = gst_adapter_available(sbcpay->adapter);
+	if (available + RTP_SBC_HEADER_TOTAL >=
+			GST_BASE_RTP_PAYLOAD_MTU(sbcpay) ||
+			(sbcpay->min_frames != -1 && available >
+			(sbcpay->min_frames * sbcpay->frame_length)))
+		return gst_rtp_sbc_pay_flush_buffers(sbcpay);
+
+	return GST_FLOW_OK;
+}
+
+static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad,
+				GstEvent *event)
+{
+	GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad));
+
+	switch (GST_EVENT_TYPE(event)) {
+	case GST_EVENT_EOS:
+		gst_rtp_sbc_pay_flush_buffers(sbcpay);
+		break;
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+static void gst_rtp_sbc_pay_base_init(gpointer g_class)
+{
+	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+	gst_element_class_add_pad_template(element_class,
+		gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory));
+	gst_element_class_add_pad_template(element_class,
+		gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory));
+
+	gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details);
+}
+
+static void gst_rtp_sbc_pay_finalize(GObject *object)
+{
+	GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object);
+	g_object_unref (sbcpay->adapter);
+
+	GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass)
+{
+	GObjectClass *gobject_class;
+	GstBaseRTPPayloadClass *payload_class =
+		GST_BASE_RTP_PAYLOAD_CLASS(klass);
+
+	gobject_class = G_OBJECT_CLASS(klass);
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize);
+	gobject_class->set_property = GST_DEBUG_FUNCPTR(
+			gst_rtp_sbc_pay_set_property);
+	gobject_class->get_property = GST_DEBUG_FUNCPTR(
+			gst_rtp_sbc_pay_get_property);
+
+	payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps);
+	payload_class->handle_buffer = GST_DEBUG_FUNCPTR(
+			gst_rtp_sbc_pay_handle_buffer);
+	payload_class->handle_event = GST_DEBUG_FUNCPTR(
+			gst_rtp_sbc_pay_handle_event);
+
+	/* properties */
+	g_object_class_install_property (G_OBJECT_CLASS (klass),
+		PROP_MIN_FRAMES,
+		g_param_spec_int ("min-frames", "minimum frame number",
+		"Minimum quantity of frames to send in one packet "
+		"(-1 for maximum allowed by the mtu)",
+		-1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE));
+
+	GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0,
+				"RTP SBC payloader");
+}
+
+static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id,
+			const GValue * value, GParamSpec * pspec)
+{
+	GstRtpSBCPay *sbcpay;
+
+	sbcpay = GST_RTP_SBC_PAY (object);
+
+	switch (prop_id) {
+	case PROP_MIN_FRAMES:
+		sbcpay->min_frames = g_value_get_int(value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	break;
+	}
+}
+
+static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id,
+                        GValue * value, GParamSpec * pspec)
+{
+	GstRtpSBCPay *sbcpay;
+
+	sbcpay = GST_RTP_SBC_PAY (object);
+
+	switch (prop_id) {
+	case PROP_MIN_FRAMES:
+		g_value_set_int(value, sbcpay->min_frames);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	break;
+	}
+}
+
+static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass)
+{
+	self->adapter = gst_adapter_new();
+	self->frame_length = 0;
+
+	self->min_frames = DEFAULT_MIN_FRAMES;
+}
+
+gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin)
+{
+	return gst_element_register (plugin, "rtpsbcpay",
+			GST_RANK_NONE, GST_TYPE_RTP_SBC_PAY);
+}
+
diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h
new file mode 100644
index 0000000..f086a1c
--- /dev/null
+++ b/audio/gstrtpsbcpay.h
@@ -0,0 +1,65 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2007  Marcel Holtmann <[email protected]>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include <gst/base/gstadapter.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_SBC_PAY \
+	(gst_rtp_sbc_pay_get_type())
+#define GST_RTP_SBC_PAY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\
+		GstRtpSBCPay))
+#define GST_RTP_SBC_PAY_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\
+		GstRtpSBCPayClass))
+#define GST_IS_RTP_SBC_PAY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY))
+#define GST_IS_RTP_SBC_PAY_CLASS(obj) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY))
+
+typedef struct _GstRtpSBCPay GstRtpSBCPay;
+typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass;
+
+struct _GstRtpSBCPay {
+	GstBaseRTPPayload base;
+
+	GstAdapter *adapter;
+
+	guint frame_length;
+
+	guint min_frames;
+};
+
+struct _GstRtpSBCPayClass {
+	GstBaseRTPPayloadClass parent_class;
+};
+
+GType gst_rtp_sbc_pay_get_type(void);
+
+gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
-- 
1.5.3.7
0004-Fix-bitrate-in-mpeg12-to-be-2-bytes-long.patch (text/x-diff, 1.8 KB)
From 26155d22dac47ae46ce75b073ae4c9a4e4d4a54c Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Wed, 12 Dec 2007 18:40:00 -0300
Subject: [PATCH] Fix bitrate in mpeg12 to be 2 bytes long.

---
 audio/a2dp.h |    8 ++------
 audio/ipc.h  |    1 -
 audio/unix.c |    4 +---
 3 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/audio/a2dp.h b/audio/a2dp.h
index cfd1c47..5508c96 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -83,9 +83,7 @@ struct mpeg_codec_cap {
 	uint8_t frequency:6;
 	uint8_t mpf:1;
 	uint8_t rfa:1;
-	uint8_t bitrate0:7;
-	uint8_t vbr:1;
-	uint8_t bitrate1;
+	uint16_t bitrate;
 } __attribute__ ((packed));
 
 #elif __BYTE_ORDER == __BIG_ENDIAN
@@ -109,9 +107,7 @@ struct mpeg_codec_cap {
 	uint8_t rfa:1;
 	uint8_t mpf:1;
 	uint8_t frequency:6;
-	uint8_t vbr:1;
-	uint8_t bitrate0:7;
-	uint8_t bitrate1;
+	uint16_t bitrate;
 } __attribute__ ((packed));
 
 #else
diff --git a/audio/ipc.h b/audio/ipc.h
index 64ed65a..1f80a47 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -168,7 +168,6 @@ typedef struct {
 	uint8_t layer;
 	uint8_t frequency;
 	uint8_t mpf;
-	uint8_t vbr;
 	uint16_t bitrate;
 } __attribute__ ((packed)) mpeg_capabilities_t;
 
diff --git a/audio/unix.c b/audio/unix.c
index 2226579..d12699e 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -389,9 +389,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
 		rsp->mpeg_capabilities.layer = mpeg_cap->layer;
 		rsp->mpeg_capabilities.frequency = mpeg_cap->frequency;
 		rsp->mpeg_capabilities.mpf = mpeg_cap->mpf;
-		rsp->mpeg_capabilities.vbr = mpeg_cap->vbr;
-		rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 &
-				mpeg_cap->bitrate0 << 8;
+		rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate;
 	}
 
 	unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h);
-- 
1.5.3.7
0005-Add-defines-for-mpeg12-codec.patch (text/x-diff, 8.4 KB)
From 010162374c3fc9a1962e88fa82f046e9f71c1002 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Thu, 13 Dec 2007 11:33:20 -0300
Subject: [PATCH] Add defines for mpeg12 codec.

---
 audio/gsta2dpsendersink.c |   37 ++++++++++++++++++++++++-------------
 audio/ipc.h               |   19 +++++++++++++++----
 audio/pcm_bluetooth.c     |   39 ++++++++++++++++++++++-----------------
 3 files changed, 61 insertions(+), 34 deletions(-)

diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c
index cfb67b8..1f54f29 100644
--- a/audio/gsta2dpsendersink.c
+++ b/audio/gsta2dpsendersink.c
@@ -236,13 +236,13 @@ static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink,
 	value = gst_structure_get_value(structure, "rate");
 	rate = g_value_get_int(value);
 	if (rate == 44100)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100;
+		cfg->frequency = BT_SBC_SAMPLING_FREQ_44100;
 	else if (rate == 48000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000;
+		cfg->frequency = BT_SBC_SAMPLING_FREQ_48000;
 	else if (rate == 32000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000;
+		cfg->frequency = BT_SBC_SAMPLING_FREQ_32000;
 	else if (rate == 16000)
-		cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000;
+		cfg->frequency = BT_SBC_SAMPLING_FREQ_16000;
 	else {
 		GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
 		return FALSE;
@@ -396,17 +396,14 @@ static gboolean server_callback(GIOChannel *chan,
 	return TRUE;
 }
 
-static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
+static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps(
+			GstA2dpSenderSink *self, sbc_capabilities_t *sbc)
 {
-	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
 	GstStructure *structure;
 	GValue *value;
 	GValue *list;
-	gchar *tmp;
 	gboolean mono, stereo;
 
-	GST_LOG_OBJECT(self, "updating device caps");
-
 	structure = gst_structure_empty_new("audio/x-sbc");
 	value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
 
@@ -518,19 +515,19 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
 	/* rate */
 	g_value_init(value, G_TYPE_INT);
 	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
-	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) {
+	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_A2DP_SAMPLING_FREQ_44100) {
+	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_A2DP_SAMPLING_FREQ_32000) {
+	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_A2DP_SAMPLING_FREQ_16000) {
+	if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
 		g_value_set_int(value, 16000);
 		gst_value_list_prepend_value(list, value);
 	}
@@ -584,6 +581,20 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
 	gst_structure_set_value(structure, "channels", value);
 	g_free(value);
 
+	return structure;
+}
+
+static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
+{
+	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
+	mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities;
+	GstStructure *structure;
+	gchar *tmp;
+
+	GST_LOG_OBJECT(self, "updating device caps");
+
+	structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc);
+
 	if (self->dev_caps != NULL)
 		gst_caps_unref(self->dev_caps);
 	self->dev_caps = gst_caps_new_full(structure, NULL);
diff --git a/audio/ipc.h b/audio/ipc.h
index 1f80a47..3768dcf 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -129,10 +129,10 @@ struct bt_getcapabilities_req {
  * SBC Codec parameters as per A2DP profile 1.0 § 4.3
  */
 
-#define BT_A2DP_SAMPLING_FREQ_16000		(1 << 3)
-#define BT_A2DP_SAMPLING_FREQ_32000		(1 << 2)
-#define BT_A2DP_SAMPLING_FREQ_44100		(1 << 1)
-#define BT_A2DP_SAMPLING_FREQ_48000		1
+#define BT_SBC_SAMPLING_FREQ_16000		(1 << 3)
+#define BT_SBC_SAMPLING_FREQ_32000		(1 << 2)
+#define BT_SBC_SAMPLING_FREQ_44100		(1 << 1)
+#define BT_SBC_SAMPLING_FREQ_48000		1
 
 #define BT_A2DP_CHANNEL_MODE_MONO		(1 << 3)
 #define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
@@ -152,6 +152,17 @@ struct bt_getcapabilities_req {
 #define BT_A2DP_ALLOCATION_LOUDNESS		1
 #define BT_A2DP_ALLOCATION_AUTO			0
 
+#define BT_MPEG_SAMPLING_FREQ_16000		(1 << 5)
+#define BT_MPEG_SAMPLING_FREQ_22050		(1 << 4)
+#define BT_MPEG_SAMPLING_FREQ_24000		(1 << 3)
+#define BT_MPEG_SAMPLING_FREQ_32000		(1 << 2)
+#define BT_MPEG_SAMPLING_FREQ_44100		(1 << 1)
+#define BT_MPEG_SAMPLING_FREQ_48000		1
+
+#define BT_MPEG_LAYER_1				(1 << 2)
+#define BT_MPEG_LAYER_2				(1 << 1)
+#define BT_MPEG_LAYER_3				1
+
 typedef struct {
 	uint8_t channel_mode;
 	uint8_t frequency;
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index b0e3838..d423460 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -468,10 +468,10 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io,
 static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
 {
 	switch (freq) {
-	case BT_A2DP_SAMPLING_FREQ_16000:
-	case BT_A2DP_SAMPLING_FREQ_32000:
+	case BT_SBC_SAMPLING_FREQ_16000:
+	case BT_SBC_SAMPLING_FREQ_32000:
 		return 53;
-	case BT_A2DP_SAMPLING_FREQ_44100:
+	case BT_SBC_SAMPLING_FREQ_44100:
 		switch (mode) {
 		case BT_A2DP_CHANNEL_MODE_MONO:
 		case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
@@ -483,7 +483,7 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
 			DBG("Invalid channel mode %u", mode);
 			return 53;
 		}
-	case BT_A2DP_SAMPLING_FREQ_48000:
+	case BT_SBC_SAMPLING_FREQ_48000:
 		switch (mode) {
 		case BT_A2DP_CHANNEL_MODE_MONO:
 		case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
@@ -508,16 +508,16 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate,
 
 	switch (rate) {
 	case 48000:
-		cap->frequency = BT_A2DP_SAMPLING_FREQ_48000;
+		cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
 		break;
 	case 44100:
-		cap->frequency = BT_A2DP_SAMPLING_FREQ_44100;
+		cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
 		break;
 	case 32000:
-		cap->frequency = BT_A2DP_SAMPLING_FREQ_32000;
+		cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
 		break;
 	case 16000:
-		cap->frequency = BT_A2DP_SAMPLING_FREQ_16000;
+		cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
 		break;
 	default:
 		DBG("Rate %d not supported", rate);
@@ -641,16 +641,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
 		sbc_init(&a2dp->sbc, 0);
 	a2dp->sbc_initialized = 1;
 
-	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000)
+	if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
 		a2dp->sbc.rate = 16000;
 
-	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000)
+	if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
 		a2dp->sbc.rate = 32000;
 
-	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100)
+	if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
 		a2dp->sbc.rate = 44100;
 
-	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000)
+	if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
 		a2dp->sbc.rate = 48000;
 
 	if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
@@ -659,7 +659,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
 		a2dp->sbc.channels = 2;
 
 	if (active_capabilities.channel_mode &
-			(BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+			(BT_A2DP_CHANNEL_MODE_MONO ||
+			BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
 		a2dp->sbc.joint = 1;
 	else
 		a2dp->sbc.joint = 0;
@@ -1231,22 +1232,26 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
 
 	/* supported rates */
 	rate_count = 0;
-	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) {
+	if (a2dp->sbc_capabilities.frequency &
+			BT_SBC_SAMPLING_FREQ_16000) {
 		rate_list[rate_count] = 16000;
 		rate_count++;
 	}
 
-	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) {
+	if (a2dp->sbc_capabilities.frequency &
+			BT_SBC_SAMPLING_FREQ_32000) {
 		rate_list[rate_count] = 32000;
 		rate_count++;
 	}
 
-	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) {
+	if (a2dp->sbc_capabilities.frequency &
+			BT_SBC_SAMPLING_FREQ_44100) {
 		rate_list[rate_count] = 44100;
 		rate_count++;
 	}
 
-	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) {
+	if (a2dp->sbc_capabilities.frequency &
+			BT_SBC_SAMPLING_FREQ_48000) {
 		rate_list[rate_count] = 48000;
 		rate_count++;
 	}
-- 
1.5.3.7
0006-Add-mpeg12-sep-registration.patch (text/x-diff, 10.7 KB)
From 4f9b1d51c6f6973c2deaf0bf50d1e84ec4c439d6 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Thu, 13 Dec 2007 20:01:29 -0300
Subject: [PATCH] Add mpeg12 sep registration.

---
 audio/a2dp.c  |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 audio/a2dp.h  |    5 ++
 audio/avdtp.c |    2 +
 audio/avdtp.h |    1 +
 audio/unix.c  |   45 +++++++++++++------
 5 files changed, 168 insertions(+), 26 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 1c59374..ec74300 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -58,6 +58,7 @@
 
 struct a2dp_sep {
 	uint8_t type;
+	uint8_t codec;
 	struct avdtp_local_sep *sep;
 	struct avdtp *session;
 	struct avdtp_stream *stream;
@@ -280,7 +281,7 @@ static void stream_state_changed(struct avdtp_stream *stream,
 
 }
 
-static gboolean setconf_ind(struct avdtp *session,
+static gboolean sbc_setconf_ind(struct avdtp *session,
 				struct avdtp_local_sep *sep,
 				struct avdtp_stream *stream,
 				GSList *caps, uint8_t *err,
@@ -331,7 +332,7 @@ static gboolean setconf_ind(struct avdtp *session,
 	return TRUE;
 }
 
-static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 				GSList **caps, uint8_t *err, void *user_data)
 {
 	struct a2dp_sep *a2dp_sep = user_data;
@@ -386,6 +387,84 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	return TRUE;
 }
 
+static gboolean mpeg_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 device *dev;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		debug("SBC Sink: Set_Configuration_Ind");
+	else
+		debug("SBC Source: Set_Configuration_Ind");
+
+	dev = a2dp_get_dev(session);
+	if (!dev) {
+		*err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		*category = 0x00;
+		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 mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				GSList **caps, uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct mpeg_codec_cap mpeg_cap;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		debug("SBC Sink: Get_Capability_Ind");
+	else
+		debug("SBC Source: Get_Capability_Ind");
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap));
+
+	mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+
+	mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 |
+				MPEG_SAMPLING_FREQ_44100 |
+				MPEG_SAMPLING_FREQ_32000 |
+				MPEG_SAMPLING_FREQ_24000 |
+				MPEG_SAMPLING_FREQ_22050 |
+				MPEG_SAMPLING_FREQ_16000 );
+
+	mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO |
+					MPEG_CHANNEL_MODE_STEREO |
+					MPEG_CHANNEL_MODE_DUAL_CHANNEL |
+					MPEG_CHANNEL_MODE_MONO );
+
+	mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 );
+
+	mpeg_cap.bitrate = 0xFFFF;
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+						sizeof(mpeg_cap));
+
+	*caps = g_slist_append(*caps, media_codec);
+
+	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)
@@ -797,9 +876,21 @@ static struct avdtp_sep_cfm cfm = {
 	.reconfigure		= reconf_cfm
 };
 
-static struct avdtp_sep_ind ind = {
-	.get_capability		= getcap_ind,
-	.set_configuration	= setconf_ind,
+static struct avdtp_sep_ind sbc_ind = {
+	.get_capability		= sbc_getcap_ind,
+	.set_configuration	= sbc_setconf_ind,
+	.get_configuration	= getconf_ind,
+	.open			= open_ind,
+	.start			= start_ind,
+	.suspend		= suspend_ind,
+	.close			= close_ind,
+	.abort			= abort_ind,
+	.reconfigure		= reconf_ind
+};
+
+static struct avdtp_sep_ind mpeg_ind = {
+	.get_capability		= mpeg_getcap_ind,
+	.set_configuration	= mpeg_setconf_ind,
 	.get_configuration	= getconf_ind,
 	.open			= open_ind,
 	.start			= start_ind,
@@ -880,23 +971,27 @@ static int a2dp_sink_record(sdp_buf_t *buf)
 	return 0;
 }
 
-static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type)
+static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type,
+					uint8_t codec)
 {
 	struct a2dp_sep *sep;
 	GSList **l;
 	int (*create_record)(sdp_buf_t *buf);
 	uint32_t *record_id;
 	sdp_buf_t buf;
+	struct avdtp_sep_ind *ind;
 
 	sep = g_new0(struct a2dp_sep, 1);
 
-	sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO,
-					&ind, &cfm, sep);
+	ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+	sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, codec,
+					ind, &cfm, sep);
 	if (sep->sep == NULL) {
 		g_free(sep);
 		return NULL;
 	}
 
+	sep->codec = codec;
 	sep->type = type;
 
 	if (type == AVDTP_SEP_TYPE_SOURCE) {
@@ -945,11 +1040,15 @@ int a2dp_init(DBusConnection *conn, int sources, int sinks)
 
 	avdtp_init();
 
-	for (i = 0; i < sources; i++)
-		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE);
+	for (i = 0; i < sources; i++) {
+		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC);
+		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_MPEG12);
+	}
 
-	for (i = 0; i < sinks; i++)
-		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK);
+	for (i = 0; i < sinks; i++) {
+		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_SBC);
+		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_MPEG12);
+	}
 
 	return 0;
 }
@@ -1025,14 +1124,32 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,
 	struct a2dp_sep *sep = NULL;
 	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;
 
+	for (l = 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)
+		return 0;
+
 	for (l = sources; l != NULL; l = l->next) {
 		struct a2dp_sep *tmp = l->data;
 
 		if (tmp->locked)
 			continue;
 
+		if (tmp->codec != codec_cap->media_codec_type)
+			continue;
+
 		if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) {
 			sep = tmp;
 			break;
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 5508c96..84301ba 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -48,6 +48,11 @@
 #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
diff --git a/audio/avdtp.c b/audio/avdtp.c
index d543345..97868a0 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -2696,6 +2696,7 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
 }
 
 struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+						uint8_t codec_type,
 						struct avdtp_sep_ind *ind,
 						struct avdtp_sep_cfm *cfm,
 						void *user_data)
@@ -2711,6 +2712,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
 	sep->info.seid = free_seid++;
 	sep->info.type = type;
 	sep->info.media_type = media_type;
+	sep->codec = codec_type;
 	sep->ind = ind;
 	sep->cfm = cfm;
 	sep->user_data = user_data;
diff --git a/audio/avdtp.h b/audio/avdtp.h
index 241fa71..01ca50c 100644
--- a/audio/avdtp.h
+++ b/audio/avdtp.h
@@ -242,6 +242,7 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream);
 int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
 
 struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+						uint8_t codec_type,
 						struct avdtp_sep_ind *ind,
 						struct avdtp_sep_cfm *cfm,
 						void *user_data);
diff --git a/audio/unix.c b/audio/unix.c
index d12699e..f4d29d2 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -792,6 +792,7 @@ static void handle_setconfiguration_req(struct unix_client *client,
 {
 	struct avdtp_service_capability *media_transport, *media_codec;
 	struct sbc_codec_cap sbc_cap;
+	struct mpeg_codec_cap mpeg_cap;
 	struct device *dev;
 	bdaddr_t bdaddr;
 	int err = 0;
@@ -840,20 +841,36 @@ static void handle_setconfiguration_req(struct unix_client *client,
 
 	client->caps = g_slist_append(client->caps, media_transport);
 
-	memset(&sbc_cap, 0, sizeof(sbc_cap));
-
-	sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
-	sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
-	sbc_cap.channel_mode = req->sbc_capabilities.channel_mode;
-	sbc_cap.frequency = req->sbc_capabilities.frequency;
-	sbc_cap.allocation_method = req->sbc_capabilities.allocation_method;
-	sbc_cap.subbands = req->sbc_capabilities.subbands;
-	sbc_cap.block_length = req->sbc_capabilities.block_length ;
-	sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool;
-	sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool;
-
-	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
-						sizeof(sbc_cap));
+	if (req->mpeg_capabilities.frequency) {
+		memset(&mpeg_cap, 0, sizeof(mpeg_cap));
+
+		mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+		mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+		mpeg_cap.channel_mode = req->mpeg_capabilities.channel_mode;
+		mpeg_cap.crc = req->mpeg_capabilities.crc;
+		mpeg_cap.layer = req->mpeg_capabilities.layer;
+		mpeg_cap.frequency = req->mpeg_capabilities.frequency;
+		mpeg_cap.mpf = req->mpeg_capabilities.mpf;
+		mpeg_cap.bitrate = req->mpeg_capabilities.bitrate;
+
+		media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+							sizeof(mpeg_cap));
+	} else {
+		memset(&sbc_cap, 0, sizeof(sbc_cap));
+
+		sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+		sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+		sbc_cap.channel_mode = req->sbc_capabilities.channel_mode;
+		sbc_cap.frequency = req->sbc_capabilities.frequency;
+		sbc_cap.allocation_method = req->sbc_capabilities.allocation_method;
+		sbc_cap.subbands = req->sbc_capabilities.subbands;
+		sbc_cap.block_length = req->sbc_capabilities.block_length;
+		sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool;
+		sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool;
+
+		media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+							sizeof(sbc_cap));
+	}
 
 	client->caps = g_slist_append(client->caps, media_codec);
 
-- 
1.5.3.7
0007-Fix-bug-selecting-SEPs.patch (text/x-diff, 1.9 KB)
From 5e0f114b83fd6e0434a25fb141dd795ac9d2712e Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Fri, 21 Dec 2007 16:08:21 -0300
Subject: [PATCH] Fix bug selecting SEPs.

---
 audio/a2dp.c |    3 ++-
 audio/unix.c |   13 +++++++++++++
 2 files changed, 15 insertions(+), 1 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index ec74300..b1409f5 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1184,7 +1184,8 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,
 	switch (avdtp_sep_get_state(sep->sep)) {
 	case AVDTP_STATE_IDLE:
 		if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK,
-				AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC,
+				codec_cap->media_type,
+				codec_cap->media_codec_type,
 				&lsep, &rsep) < 0) {
 			error("No matching ACP and INT SEPs found");
 			goto failed;
diff --git a/audio/unix.c b/audio/unix.c
index f4d29d2..d71c420 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -855,6 +855,12 @@ static void handle_setconfiguration_req(struct unix_client *client,
 
 		media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
 							sizeof(mpeg_cap));
+
+		info("config mpeg - frequency = %u channel_mode = %u "
+			"layer = %u crc = %u mpf = %u bitrate = %u",
+			mpeg_cap.frequency, mpeg_cap.channel_mode,
+			mpeg_cap.layer, mpeg_cap.crc, mpeg_cap.mpf,
+			mpeg_cap.bitrate);
 	} else {
 		memset(&sbc_cap, 0, sizeof(sbc_cap));
 
@@ -870,6 +876,13 @@ static void handle_setconfiguration_req(struct unix_client *client,
 
 		media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
 							sizeof(sbc_cap));
+
+		info("config sbc - frequency = %u channel_mode = %u "
+			"allocation = %u subbands = %u blocks = %u "
+			"bitpool = %u", sbc_cap.frequency,
+			sbc_cap.channel_mode, sbc_cap.allocation_method,
+			sbc_cap.subbands, sbc_cap.block_length,
+			sbc_cap.max_bitpool);
 	}
 
 	client->caps = g_slist_append(client->caps, media_codec);
-- 
1.5.3.7
0008-Add-mp3-support-for-gstreamer-plugin.patch (text/x-diff, 20.8 KB)
From 624e9758f56cfe1c7305bcc318954ae400aa2375 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Wed, 16 Jan 2008 14:00:34 -0300
Subject: [PATCH] Add mp3 support for gstreamer plugin.

---
 audio/gsta2dpsendersink.c |  336 ++++++++++++++++++++++++++++++++++++++++++++-
 audio/gsta2dpsendersink.h |   11 ++
 audio/gsta2dpsink.c       |  105 ++++++++++++++-
 audio/gsta2dpsink.h       |    4 +
 audio/gstsbcutil.c        |   18 ++-
 5 files changed, 461 insertions(+), 13 deletions(-)

diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c
index 1f54f29..d0d8d59 100644
--- a/audio/gsta2dpsendersink.c
+++ b/audio/gsta2dpsendersink.c
@@ -35,6 +35,8 @@
 
 #include <bluetooth/bluetooth.h>
 
+#include <gst/rtp/gstrtpbuffer.h>
+
 #include "ipc.h"
 #include "rtp.h"
 #include "gstsbcutil.h"
@@ -46,6 +48,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug);
 
 #define BUFFER_SIZE 2048
 #define TEMPLATE_MAX_BITPOOL 64
+#define CRC_PROTECTED 1
+#define CRC_UNPROTECTED 0
 
 #define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START {	\
 		g_mutex_lock (s->sink_lock);		\
@@ -84,7 +88,15 @@ static GstStaticPadTemplate a2dp_sender_sink_factory =
 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
 		GST_STATIC_CAPS("application/x-rtp, "
 				"media = (string) \"audio\", "
-				"encoding-name = (string) \"SBC\""
+				"encoding-name = (string) \"SBC\";"
+				"application/x-rtp, "
+				"media = (string) \"audio\", "
+				"payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", "
+				"clock-rate = (int) 90000; "
+				"application/x-rtp, "
+				"media = (string) \"audio\", "
+				"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+				"clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\""
 				));
 
 static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self,
@@ -584,20 +596,145 @@ static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps(
 	return structure;
 }
 
+static GstStructure *gst_a2dp_sender_sink_parse_mpeg_caps(
+			GstA2dpSenderSink *self, mpeg_capabilities_t *mpeg)
+{
+	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_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self)
 {
 	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
 	mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities;
-	GstStructure *structure;
+	GstStructure *sbc_structure;
+	GstStructure *mpeg_structure;
 	gchar *tmp;
 
 	GST_LOG_OBJECT(self, "updating device caps");
 
-	structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc);
+	sbc_structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc);
+	mpeg_structure = gst_a2dp_sender_sink_parse_mpeg_caps(self, mpeg);
 
 	if (self->dev_caps != NULL)
 		gst_caps_unref(self->dev_caps);
-	self->dev_caps = gst_caps_new_full(structure, NULL);
+	self->dev_caps = gst_caps_new_full(sbc_structure, NULL);
+	if (mpeg_structure != NULL)
+		gst_caps_append_structure(self->dev_caps, mpeg_structure);
 
 	tmp = gst_caps_to_string(self->dev_caps);
 	GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp);
@@ -623,6 +760,7 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self)
 	io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h);
 	if (io_error != G_IO_ERROR_NONE) {
 		GST_ERROR_OBJECT(self, "Error while asking device caps");
+		return FALSE;
 	}
 
 	io_error = gst_a2dp_sender_sink_audioservice_expect(self,
@@ -648,6 +786,72 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self)
 	return TRUE;
 }
 
+static gint gst_a2dp_sender_sink_get_channel_mode(const gchar *mode)
+{
+	if (strcmp(mode, "stereo") == 0)
+		return BT_A2DP_CHANNEL_MODE_STEREO;
+	else if (strcmp(mode, "joint") == 0)
+		return BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+	else if (strcmp(mode, "dual") == 0)
+		return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+	else if (strcmp(mode, "mono") == 0)
+		return BT_A2DP_CHANNEL_MODE_MONO;
+	else
+		return -1;
+}
+
+static void gst_a2dp_sender_sink_tag(const GstTagList *taglist,
+			const gchar* tag, gpointer user_data)
+{
+	gboolean crc;
+	gchar *channel_mode = NULL;
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(user_data);
+
+	if (strcmp(tag, "has-crc") == 0) {
+
+		if (!gst_tag_list_get_boolean(taglist, tag, &crc)) {
+			GST_WARNING_OBJECT(self, "failed to get crc tag");
+			self->mpeg_stream_changed = TRUE;
+		}
+
+		gst_a2dp_sender_sink_set_crc(self, crc);
+
+	} else if (strcmp(tag, "channel-mode") == 0) {
+
+		if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) {
+			GST_WARNING_OBJECT(self, "failed to get channel-mode tag");
+			self->mpeg_stream_changed = TRUE;
+		}
+
+		self->channel_mode = gst_a2dp_sender_sink_get_channel_mode(
+					channel_mode);
+		if (self->channel_mode == -1)
+			GST_WARNING_OBJECT(self, "Received invalid channel "
+					"mode: %s", channel_mode);
+		g_free(channel_mode);
+
+	} else
+		GST_DEBUG_OBJECT(self, "received unused tag: %s", tag);
+}
+
+static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink,
+			GstEvent *event)
+{
+	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
+	GstTagList *taglist = NULL;
+
+	if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
+		/* we check the tags, mp3 has tags that are importants and
+		 * are outside caps */
+		gst_event_parse_tag(event, &taglist);
+		gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self);
+		/*g_free(taglist); FIXME this is causing segfault*/
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink)
 {
 	GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink);
@@ -675,6 +879,9 @@ static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink)
 
 	self->stream = NULL;
 	self->stream_caps = NULL;
+	self->mp3_using_crc = -1;
+	self->channel_mode = -1;
+	self->mpeg_stream_changed = FALSE;
 
 	if (!gst_a2dp_sender_sink_get_capabilities(self))
 		goto failed;
@@ -739,6 +946,75 @@ static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self)
 	return TRUE;
 }
 
+static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf(
+		GstA2dpSenderSink *self, GstCaps *caps,
+		mpeg_capabilities_t *pkt)
+{
+	const GValue *value = NULL;
+	gint rate, layer;
+	GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+	/* layer */
+	value = gst_structure_get_value(structure, "layer");
+	layer = g_value_get_int(value);
+	if (layer == 1)
+		pkt->layer = BT_MPEG_LAYER_1;
+	else if (layer == 2)
+		pkt->layer = BT_MPEG_LAYER_2;
+	else if (layer == 3)
+		pkt->layer = BT_MPEG_LAYER_3;
+	else {
+		GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer);
+		return FALSE;
+	}
+
+	/* crc */
+	if (self->mp3_using_crc != -1)
+		pkt->crc = self->mp3_using_crc;
+	else {
+		GST_ERROR_OBJECT(self, "No info about crc was received, can't proceed");
+		return FALSE;
+	}
+
+	/* channel mode */
+	if (self->channel_mode != -1)
+		pkt->channel_mode = self->channel_mode;
+	else {
+		GST_ERROR_OBJECT(self, "No info about channel mode received, can't proceed");
+		return FALSE;
+	}
+
+	/* mpf - we will only use the mandatory one */
+	pkt->mpf = 0;
+
+	value = gst_structure_get_value(structure, "rate");
+	rate = g_value_get_int(value);
+	if (rate == 44100)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100;
+	else if (rate == 48000)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000;
+	else if (rate == 32000)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000;
+	else if (rate == 24000)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000;
+	else if (rate == 22050)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050;
+	else if (rate == 16000)
+		pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000;
+	else {
+		GST_ERROR_OBJECT(self, "Invalid rate while setting caps");
+		return FALSE;
+	}
+
+	/* vbr - we always say its vbr, we don't have how to know it */
+	pkt->bitrate = 0x8000;
+
+	/* bitrate - we don't set anything, its vbr */
+	/* FIXME - is this right? */
+
+	return TRUE;
+}
+
 static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self,
 			GstCaps *caps)
 {
@@ -748,6 +1024,7 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self,
 	gboolean ret;
 	GIOError io_error;
 	gchar *temp;
+	GstStructure *structure;
 
 	temp = gst_caps_to_string(caps);
 	GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
@@ -757,8 +1034,17 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self,
 	req->h.msg_type = BT_SETCONFIGURATION_REQ;
 	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
 	strncpy(req->device, self->device, 18);
-	ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps,
-			&req->sbc_capabilities);
+	structure = gst_caps_get_structure(caps, 0);
+
+	if (gst_structure_has_name(structure, "audio/x-sbc"))
+		ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps,
+				&req->sbc_capabilities);
+	else if (gst_structure_has_name(structure, "audio/mpeg"))
+		ret = gst_a2dp_sender_sink_init_mp3_pkt_conf(self, caps,
+				&req->mpeg_capabilities);
+	else
+		ret = FALSE;
+
 	if (!ret) {
 		GST_ERROR_OBJECT(self, "Couldn't parse caps "
 				"to packet configuration");
@@ -884,6 +1170,8 @@ static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass)
 					gst_a2dp_sender_sink_preroll);
 	basesink_class->unlock = GST_DEBUG_FUNCPTR(
 					gst_a2dp_sender_sink_unlock);
+	basesink_class->event = GST_DEBUG_FUNCPTR(
+					gst_a2dp_sender_sink_event);
 
 	basesink_class->buffer_alloc =
 		GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc);
@@ -1022,3 +1310,39 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self)
 	return g_strdup(self->device);
 }
 
+void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc)
+{
+	gint new_crc;
+
+	new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED;
+
+	/* test if we already received a different crc */
+	if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) {
+		GST_ERROR_OBJECT(self, "crc changed during stream");
+		/* FIXME test this, its not being used anywhere */
+		self->mpeg_stream_changed = TRUE;
+		return;
+	}
+	self->mp3_using_crc = new_crc;
+
+}
+
+void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self,
+			const gchar *mode)
+{
+	gint new_mode;
+
+	new_mode = gst_a2dp_sender_sink_get_channel_mode(mode);
+
+	if (self->channel_mode != -1 && new_mode != self->channel_mode) {
+		GST_ERROR_OBJECT(self, "channel mode changed during stream");
+		self->mpeg_stream_changed = TRUE;
+	}
+
+	self->channel_mode = new_mode;
+	if (self->channel_mode == -1)
+		GST_WARNING_OBJECT(self, "Received invalid channel "
+				"mode: %s", mode);
+}
+
+
diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h
index 863ef6c..f23c86b 100644
--- a/audio/gsta2dpsendersink.h
+++ b/audio/gsta2dpsendersink.h
@@ -56,6 +56,11 @@ struct _GstA2dpSenderSink {
 	struct bluetooth_data *data;
 	GIOChannel *server;
 
+	/* mp3 stream data (outside caps data)*/
+	gboolean mpeg_stream_changed;
+	gint mp3_using_crc;
+	gint channel_mode;
+
 	/* stream connection data */
 	GstCaps *stream_caps;
 
@@ -85,6 +90,12 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink);
 
 gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin);
 
+void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc);
+
+void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self,
+			const gchar *mode);
+
+
 G_END_DECLS
 
 #endif /* __GST_A2DP_SENDER_SINK_H */
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index d90909c..83c907e 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -61,8 +61,11 @@ static GstStaticPadTemplate gst_a2dp_sink_factory =
 				"allocation = (string) { snr, loudness }, "
 				"bitpool = (int) [ 2, "
 				TEMPLATE_MAX_BITPOOL_STR " ]; "
+				"audio/mpeg;"
 				));
 
+/* FIXME we need a finalize */
+
 static void gst_a2dp_sink_base_init(gpointer g_class)
 {
 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
@@ -132,6 +135,10 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 			gst_event_unref(self->newseg_event);
 			self->newseg_event = NULL;
 		}
+		if (self->taglist) {
+			gst_tag_list_free(self->taglist);
+			self->taglist = NULL;
+		}
 		break;
 
 	default:
@@ -292,10 +299,63 @@ cleanup_and_fail:
 	return FALSE;
 }
 
+static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
+{
+	GstElement *rtppay;
+
+	/* FIXME we will need a internal mpegparse for identifying
+	 * stream stuff */
+
+	rtppay = gst_element_factory_make("rtpmpapay", "rtp");
+	if (rtppay == NULL) {
+		GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay");
+		return FALSE;
+	}
+
+	if (!gst_bin_add(GST_BIN(self), rtppay)) {
+		GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin");
+		goto cleanup_and_fail;
+	}
+
+	if (gst_element_set_state(rtppay, GST_STATE_READY) ==
+			GST_STATE_CHANGE_FAILURE) {
+		GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready");
+		goto remove_element_and_fail;
+	}
+
+	if (!gst_element_link(self->capsfilter, rtppay)) {
+		GST_ERROR_OBJECT(self, "couldn't link capsfilter "
+			"to rtpmpapay");
+		goto remove_element_and_fail;
+	}
+
+	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+
+	gst_element_set_state(rtppay, GST_STATE_PAUSED);
+
+	return TRUE;
+
+remove_element_and_fail:
+	gst_element_set_state (rtppay, GST_STATE_NULL);
+	gst_bin_remove(GST_BIN(self), rtppay);
+	return FALSE;
+
+cleanup_and_fail:
+	if (rtppay != NULL)
+		g_object_unref(G_OBJECT(rtppay));
+
+	return FALSE;
+}
+
+
 static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 {
 	GstA2dpSink *self;
 	GstStructure *structure;
+	GstEvent *event;
+	GstPad *capsfilterpad;
+	gboolean crc;
+	gchar *mode = NULL;
 
 	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
 	GST_INFO_OBJECT(self, "setting caps");
@@ -303,9 +363,13 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 	structure = gst_caps_get_structure(caps, 0);
 
 	/* first, we need to create our rtp payloader */
-	if (gst_structure_has_name(structure, "audio/x-sbc"))
-		gst_a2dp_sink_init_rtp_sbc_element(self);
-	else {
+	if (gst_structure_has_name(structure, "audio/x-sbc")) {
+		if(!gst_a2dp_sink_init_rtp_sbc_element(self))
+			return FALSE;
+	} else if (gst_structure_has_name(structure, "audio/mpeg")) {
+		if(!gst_a2dp_sink_init_rtp_mpeg_element(self))
+			return FALSE;
+	} else {
 		GST_ERROR_OBJECT(self, "Unexpected media type");
 		return FALSE;
 	}
@@ -313,6 +377,27 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 	if (!gst_a2dp_sink_init_sender_sink(self))
 		return FALSE;
 
+	/* check if we should push the taglist FIXME should we push this?
+	 * we can send the tags directly if needed */
+	if (self->taglist != NULL &&
+			gst_structure_has_name(structure, "audio/mpeg")) {
+
+		event = gst_event_new_tag(self->taglist);
+
+		/* send directly the crc */
+		if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
+			gst_a2dp_sender_sink_set_crc(self->sink, crc);
+
+		if (gst_tag_list_get_string(self->taglist, "channel-mode",
+				&mode))
+			gst_a2dp_sender_sink_set_channel_mode(self->sink, mode);
+
+		capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
+		gst_pad_send_event(capsfilterpad, event);
+		/*gst_tag_list_free(self->taglist);*/
+		self->taglist = NULL;
+	}
+
 	if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps))
 		return FALSE;
 
@@ -332,6 +417,7 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
 {
 	GstA2dpSink *self;
+	GstTagList *taglist = NULL;
 
 	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
 
@@ -341,6 +427,17 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
 		if (self->newseg_event != NULL)
 			gst_event_unref(self->newseg_event);
 		self->newseg_event = gst_event_ref(event);
+	} else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
+			gst_element_get_parent(GST_ELEMENT(self->sink)) !=
+			GST_OBJECT_CAST(self)) {
+		if (self->taglist == NULL) {
+			gst_event_parse_tag(event, &self->taglist);
+		} else {
+			gst_event_parse_tag(event, &taglist);
+			gst_tag_list_insert(self->taglist, taglist,
+					GST_TAG_MERGE_REPLACE);
+		}
+		/* FIXME handle tag events */
 	}
 
 	return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
@@ -374,7 +471,9 @@ static void gst_a2dp_sink_init(GstA2dpSink *self,
 	self->rtp = NULL;
 	self->device = NULL;
 	self->capsfilter = NULL;
+	self->mpegparse = NULL;
 	self->newseg_event = NULL;
+	self->taglist = gst_tag_list_new();
 
 	/* we initialize our capsfilter */
 	gst_a2dp_sink_init_caps_filter(self);
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index 4bf9d60..f74185e 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -47,6 +47,7 @@ struct _GstA2dpSink {
 	GstBaseRTPPayload *rtp;
 	GstA2dpSenderSink *sink;
 	GstElement *capsfilter;
+	GstElement *mpegparse;
 
 	gchar *device;
 
@@ -55,6 +56,9 @@ struct _GstA2dpSink {
 	GstPadEventFunction ghostpad_eventfunc;
 
 	GstEvent *newseg_event;
+	/* Store the tags received before the a2dpsender sink is created
+	 * when it is created we forward this to it */
+	GstTagList *taglist;
 };
 
 struct _GstA2dpSinkClass {
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
index d791a8d..c4d602f 100644
--- a/audio/gstsbcutil.c
+++ b/audio/gstsbcutil.c
@@ -89,12 +89,22 @@ const gchar *gst_sbc_get_allocation_from_list(const GValue *value)
 
 /*
  * Selects one mode from the ones on the list
- * TODO - use a better aproach
  */
-const gchar *gst_sbc_get_mode_from_list(const GValue *value)
+const gchar *gst_sbc_get_mode_from_list(const GValue *list)
 {
-	guint size = gst_value_list_get_size(value);
-	return g_value_get_string(gst_value_list_get_value(value, size-1));
+	int i;
+	const GValue *value;
+	const gchar *aux;
+
+	guint size = gst_value_list_get_size(list);
+	for (i = 0; i < size; i++) {
+		value = gst_value_list_get_value(list, i);
+		aux = g_value_get_string(value);
+		if (strcmp("stereo", aux) == 0) {
+			return "stereo";
+		}
+	}
+	return g_value_get_string(gst_value_list_get_value(list, size-1));
 }
 
 gint gst_sbc_get_allocation_mode_int(const gchar *allocation)
-- 
1.5.3.7
0009-Add-fixes-to-gstreamer-typefind.patch (text/x-diff, 13.1 KB)
From 9ec779a87796a18efb18a582510dabc1be2730a3 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Thu, 17 Jan 2008 19:15:38 -0300
Subject: [PATCH] Add fixes to gstreamer typefind.

---
 audio/gsta2dpsendersink.c |   27 ++++++----
 audio/gsta2dpsink.c       |  121 ++++++++++++++++++++++++++++++---------------
 audio/gstbluetooth.c      |   23 ++++++++-
 audio/gstsbcutil.c        |   55 ++++++++++++++++++++
 audio/gstsbcutil.h        |    1 +
 5 files changed, 175 insertions(+), 52 deletions(-)

diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c
index d0d8d59..0256bb9 100644
--- a/audio/gsta2dpsendersink.c
+++ b/audio/gsta2dpsendersink.c
@@ -91,12 +91,15 @@ static GstStaticPadTemplate a2dp_sender_sink_factory =
 				"encoding-name = (string) \"SBC\";"
 				"application/x-rtp, "
 				"media = (string) \"audio\", "
-				"payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", "
+				"payload = (int) "
+				GST_RTP_PAYLOAD_MPA_STRING ", "
 				"clock-rate = (int) 90000; "
 				"application/x-rtp, "
 				"media = (string) \"audio\", "
-				"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
-				"clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\""
+				"payload = (int) "
+				GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+				"clock-rate = (int) 90000, "
+				"encoding-name = (string) \"MPA\""
 				));
 
 static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self,
@@ -819,7 +822,8 @@ static void gst_a2dp_sender_sink_tag(const GstTagList *taglist,
 	} else if (strcmp(tag, "channel-mode") == 0) {
 
 		if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) {
-			GST_WARNING_OBJECT(self, "failed to get channel-mode tag");
+			GST_WARNING_OBJECT(self,
+				"failed to get channel-mode tag");
 			self->mpeg_stream_changed = TRUE;
 		}
 
@@ -845,11 +849,9 @@ static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink,
 		 * are outside caps */
 		gst_event_parse_tag(event, &taglist);
 		gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self);
-		/*g_free(taglist); FIXME this is causing segfault*/
-		return TRUE;
 	}
 
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink)
@@ -883,8 +885,11 @@ static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink)
 	self->channel_mode = -1;
 	self->mpeg_stream_changed = FALSE;
 
-	if (!gst_a2dp_sender_sink_get_capabilities(self))
+	if (!gst_a2dp_sender_sink_get_capabilities(self)) {
+		GST_ERROR_OBJECT(self, "failed to get capabilities "
+				"from device");
 		goto failed;
+	}
 
 	return TRUE;
 
@@ -972,7 +977,8 @@ static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf(
 	if (self->mp3_using_crc != -1)
 		pkt->crc = self->mp3_using_crc;
 	else {
-		GST_ERROR_OBJECT(self, "No info about crc was received, can't proceed");
+		GST_ERROR_OBJECT(self, "No info about crc was received, "
+				" can't proceed");
 		return FALSE;
 	}
 
@@ -980,7 +986,8 @@ static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf(
 	if (self->channel_mode != -1)
 		pkt->channel_mode = self->channel_mode;
 	else {
-		GST_ERROR_OBJECT(self, "No info about channel mode received, can't proceed");
+		GST_ERROR_OBJECT(self, "No info about channel mode "
+				"received, can't proceed");
 		return FALSE;
 	}
 
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index 83c907e..091d547 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -64,7 +64,10 @@ static GstStaticPadTemplate gst_a2dp_sink_factory =
 				"audio/mpeg;"
 				));
 
-/* FIXME we need a finalize */
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
 
 static void gst_a2dp_sink_base_init(gpointer g_class)
 {
@@ -119,18 +122,79 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
 	}
 }
 
+static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
+{
+	GstPad *capsfilter_pad;
+
+	/* we search for the capsfilter sinkpad */
+	capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
+
+	/* now we add a ghostpad */
+	self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
+		capsfilter_pad));
+	g_object_unref(capsfilter_pad);
+
+	/* the getcaps of our ghostpad must reflect the device caps */
+	gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
+				gst_a2dp_sink_get_caps);
+	self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
+	gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
+			GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
+
+	/* we need to handle events on our own and we also need the eventfunc
+	 * of the ghostpad for forwarding calls */
+	self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
+	gst_pad_set_event_function(GST_PAD(self->ghostpad),
+			gst_a2dp_sink_handle_event);
+
+	if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
+		GST_ERROR_OBJECT(self, "failed to add ghostpad");
+
+	return TRUE;
+}
+
 static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 			GstStateChange transition)
 {
+	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
 	GstA2dpSink *self = GST_A2DP_SINK(element);
 
 	switch (transition) {
+	case GST_STATE_CHANGE_READY_TO_PAUSED:
+		self->taglist = gst_tag_list_new();
+		break;
+
 	case GST_STATE_CHANGE_NULL_TO_READY:
-		gst_element_set_state(GST_ELEMENT(self->sink),
+		self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make(
+				"a2dpsendersink", "sendersink"));
+		if (self->sink == NULL) {
+			GST_WARNING_OBJECT(self, "failed to create a2dpsendersink");
+			return GST_STATE_CHANGE_FAILURE;
+		}
+
+		if (self->device != NULL)
+			gst_a2dp_sender_sink_set_device(self->sink,
+					self->device);
+
+		ret = gst_element_set_state(GST_ELEMENT(self->sink),
 			GST_STATE_READY);
 		break;
+	default:
+		break;
+	}
 
-	case GST_STATE_CHANGE_READY_TO_NULL:
+	if (ret == GST_STATE_CHANGE_FAILURE)
+		return ret;
+
+	ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
+                        transition);
+
+	switch (transition) {
+	case GST_STATE_CHANGE_PAUSED_TO_READY:
+		if (self->taglist) {
+			gst_tag_list_free(self->taglist);
+			self->taglist = NULL;
+		}
 		if (self->newseg_event != NULL) {
 			gst_event_unref(self->newseg_event);
 			self->newseg_event = NULL;
@@ -141,12 +205,17 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 		}
 		break;
 
+	case GST_STATE_CHANGE_READY_TO_NULL:
+		if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink)))
+			GST_WARNING_OBJECT(self, "Failed to remove "
+					"a2dpsendersink from bin");
+		break;
+
 	default:
 		break;
 	}
 
-	return GST_ELEMENT_CLASS(parent_class)->change_state(element,
-			transition);
+	return ret;
 }
 
 static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
@@ -288,7 +357,7 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
 	return TRUE;
 
 remove_element_and_fail:
-	gst_element_set_state (rtppay, GST_STATE_NULL);
+	gst_element_set_state(rtppay, GST_STATE_NULL);
 	gst_bin_remove(GST_BIN(self), rtppay);
 	return FALSE;
 
@@ -347,7 +416,6 @@ cleanup_and_fail:
 	return FALSE;
 }
 
-
 static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 {
 	GstA2dpSink *self;
@@ -364,10 +432,10 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 
 	/* first, we need to create our rtp payloader */
 	if (gst_structure_has_name(structure, "audio/x-sbc")) {
-		if(!gst_a2dp_sink_init_rtp_sbc_element(self))
+		if (!gst_a2dp_sink_init_rtp_sbc_element(self))
 			return FALSE;
 	} else if (gst_structure_has_name(structure, "audio/mpeg")) {
-		if(!gst_a2dp_sink_init_rtp_mpeg_element(self))
+		if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
 			return FALSE;
 	} else {
 		GST_ERROR_OBJECT(self, "Unexpected media type");
@@ -394,8 +462,8 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 
 		capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
 		gst_pad_send_event(capsfilterpad, event);
-		/*gst_tag_list_free(self->taglist);*/
 		self->taglist = NULL;
+		g_free(mode);
 	}
 
 	if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps))
@@ -465,15 +533,14 @@ failed:
 static void gst_a2dp_sink_init(GstA2dpSink *self,
 			GstA2dpSinkClass *klass)
 {
-	GstPad *capsfilter_pad;
-
 	self->sink = NULL;
 	self->rtp = NULL;
 	self->device = NULL;
 	self->capsfilter = NULL;
 	self->mpegparse = NULL;
 	self->newseg_event = NULL;
-	self->taglist = gst_tag_list_new();
+	self->taglist = NULL;
+	self->ghostpad = NULL;
 
 	/* we initialize our capsfilter */
 	gst_a2dp_sink_init_caps_filter(self);
@@ -481,34 +548,8 @@ static void gst_a2dp_sink_init(GstA2dpSink *self,
 		gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
 		NULL);
 
-	/* we search for the capsfilter sinkpad */
-	capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
+	gst_a2dp_sink_init_ghost_pad(self);
 
-	/* now we add a ghostpad */
-	self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
-		capsfilter_pad));
-	g_object_unref(capsfilter_pad);
-
-	/* the getcaps of our ghostpad must reflect the device caps */
-	gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
-				gst_a2dp_sink_get_caps);
-	self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
-	gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
-			GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
-
-	/* we need to handle events on our own and we also need the eventfunc
-	 * of the ghostpad for forwarding calls */
-	self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
-	gst_pad_set_event_function(GST_PAD(self->ghostpad),
-			gst_a2dp_sink_handle_event);
-
-	if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
-		GST_ERROR_OBJECT(self, "failed to add ghostpad");
-
-	self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make(
-			"a2dpsendersink", "sendersink"));
-	if (self->sink == NULL)
-		GST_WARNING_OBJECT(self, "failed to create a2dpsendersink");
 }
 
 gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin)
diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c
index 764bc89..eaab23d 100644
--- a/audio/gstbluetooth.c
+++ b/audio/gstbluetooth.c
@@ -25,6 +25,11 @@
 #include <config.h>
 #endif
 
+#include <gst/gst.h>
+
+#include "gstsbcutil.h"
+#include <sbc.h>
+
 #include "gstsbcenc.h"
 #include "gstsbcdec.h"
 #include "gstsbcparse.h"
@@ -38,12 +43,26 @@ static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc");
 
 static void sbc_typefind(GstTypeFind *tf, gpointer ignore)
 {
-	guint8 *data = gst_type_find_peek(tf, 0, 1);
+	GstCaps *caps;
+	guint8 *aux;
+	sbc_t sbc;
+	guint8 *data = gst_type_find_peek(tf, 0, 32);
+
+	if (sbc_init(&sbc, 0) < 0)
+		return;
 
 	if (data == NULL || *data != 0x9c)	/* SBC syncword */
 		return;
 
-	gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, SBC_CAPS);
+	aux = g_new(guint8, 32);
+	memcpy(aux, data, 32);
+	sbc_parse(&sbc, aux, 32);
+	g_free(aux);
+	caps = gst_sbc_parse_caps_from_sbc(&sbc);
+	sbc_finish(&sbc);
+
+	gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps);
+	gst_caps_unref(caps);
 }
 
 static gchar *sbc_exts[] = { "sbc", NULL };
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
index c4d602f..04b7217 100644
--- a/audio/gstsbcutil.c
+++ b/audio/gstsbcutil.c
@@ -167,6 +167,61 @@ const gchar *gst_sbc_get_allocation_string(int alloc)
 	}
 }
 
+/* channel mode */
+#define SBC_CM_MONO             0x00
+#define SBC_CM_DUAL_CHANNEL     0x01
+#define SBC_CM_STEREO           0x02
+#define SBC_CM_JOINT_STEREO     0x03
+
+/* allocation mode */
+#define SBC_AM_LOUDNESS         0x00
+#define SBC_AM_SNR              0x01
+
+const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint)
+{
+	if (channels == 2 && joint == 1)
+		return "joint";
+	else if (channels == 2 && joint == 0)
+		return "stereo";
+	else
+		return NULL;
+}
+
+const gchar *gst_sbc_get_allocation_string_from_sbc_t(int alloc)
+{
+	switch (alloc) {
+	case SBC_AM_LOUDNESS:
+		return "loudness";
+	case SBC_AM_SNR:
+		return "snr";
+	default:
+		return NULL;
+	}
+}
+
+GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc)
+{
+	GstCaps *caps;
+	const gchar *mode_str;
+	const gchar *allocation_str;
+
+	mode_str = gst_sbc_get_mode_string_from_sbc_t(sbc->channels,
+							sbc->joint);
+	allocation_str = gst_sbc_get_allocation_string_from_sbc_t(
+							sbc->allocation);
+	caps = gst_caps_new_simple("audio/x-sbc",
+				"rate", G_TYPE_INT, sbc->rate,
+				"channels", G_TYPE_INT, sbc->channels,
+				"mode", G_TYPE_STRING, mode_str,
+				"subbands", G_TYPE_INT, sbc->subbands,
+				"blocks", G_TYPE_INT, sbc->blocks,
+				"allocation", G_TYPE_STRING, allocation_str,
+				"bitpool", G_TYPE_INT, sbc->bitpool,
+				NULL);
+
+	return caps;
+}
+
 GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels)
 {
 	GstCaps *caps;
diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h
index a67bf1b..4bdb5ac 100644
--- a/audio/gstsbcutil.h
+++ b/audio/gstsbcutil.h
@@ -47,6 +47,7 @@ gint gst_sbc_get_mode_int(const gchar *mode);
 const gchar *gst_sbc_get_mode_string(int joint);
 
 GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels);
+GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc);
 
 GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message);
 
-- 
1.5.3.7
0010-Make-debug-logs-per-sep.patch (text/x-diff, 8.8 KB)
From ec37457030a779094ddef178f97c95f2a7744262 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Fri, 18 Jan 2008 16:42:07 -0300
Subject: [PATCH] Make debug logs per sep.

---
 audio/a2dp.c |   85 +++++++++++++++++++++++++++++----------------------------
 1 files changed, 43 insertions(+), 42 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index b1409f5..0173811 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -294,9 +294,9 @@ static gboolean sbc_setconf_ind(struct avdtp *session,
 	struct sbc_codec_cap *sbc_cap;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Set_Configuration_Ind");
+		debug("Sink %p: Set_Configuration_Ind", sep);
 	else
-		debug("SBC Source: Set_Configuration_Ind");
+		debug("Source %p: Set_Configuration_Ind", sep);
 
 	dev = a2dp_get_dev(session);
 	if (!dev) {
@@ -340,9 +340,9 @@ static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *se
 	struct sbc_codec_cap sbc_cap;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Get_Capability_Ind");
+		debug("Sink %p: Get_Capability_Ind", sep);
 	else
-		debug("SBC Source: Get_Capability_Ind");
+		debug("Source %p: Get_Capability_Ind", sep);
 
 	*caps = NULL;
 
@@ -397,9 +397,9 @@ static gboolean mpeg_setconf_ind(struct avdtp *session,
 	struct device *dev;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Set_Configuration_Ind");
+		debug("Sink %p: Set_Configuration_Ind", sep);
 	else
-		debug("SBC Source: Set_Configuration_Ind");
+		debug("Source %p: Set_Configuration_Ind", sep);
 
 	dev = a2dp_get_dev(session);
 	if (!dev) {
@@ -417,7 +417,8 @@ static gboolean mpeg_setconf_ind(struct avdtp *session,
 	return TRUE;
 }
 
-static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+static gboolean mpeg_getcap_ind(struct avdtp *session,
+				struct avdtp_local_sep *sep,
 				GSList **caps, uint8_t *err, void *user_data)
 {
 	struct a2dp_sep *a2dp_sep = user_data;
@@ -425,9 +426,9 @@ static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *s
 	struct mpeg_codec_cap mpeg_cap;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Get_Capability_Ind");
+		debug("Sink %p: Get_Capability_Ind", sep);
 	else
-		debug("SBC Source: Get_Capability_Ind");
+		debug("Source %p: Get_Capability_Ind", sep);
 
 	*caps = NULL;
 
@@ -475,9 +476,9 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	int ret;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Set_Configuration_Cfm");
+		debug("Sink %p: Set_Configuration_Cfm", sep);
 	else
-		debug("SBC Source: Set_Configuration_Cfm");
+		debug("Source %p: Set_Configuration_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 
@@ -515,9 +516,9 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Get_Configuration_Ind");
+		debug("Sink %p: Get_Configuration_Ind");
 	else
-		debug("SBC Source: Get_Configuration_Ind");
+		debug("Source %p: Get_Configuration_Ind");
 	return TRUE;
 }
 
@@ -528,9 +529,9 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Set_Configuration_Cfm");
+		debug("Sink %p: Set_Configuration_Cfm", sep);
 	else
-		debug("SBC Source: Set_Configuration_Cfm");
+		debug("Source %p: Set_Configuration_Cfm", sep);
 }
 
 static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -540,9 +541,9 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Open_Ind");
+		debug("Sink %p: Open_Ind", sep);
 	else
-		debug("SBC Source: Open_Ind");
+		debug("Source %p: Open_Ind", sep);
 	return TRUE;
 }
 
@@ -554,9 +555,9 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_setup *setup;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Open_Cfm");
+		debug("Sink %p: Open_Cfm", sep);
 	else
-		debug("SBC Source: Open_Cfm");
+		debug("Source %p: Open_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 	if (!setup)
@@ -601,9 +602,9 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Start_Ind");
+		debug("Sink %p: Start_Ind", sep);
 	else
-		debug("SBC Source: Start_Ind");
+		debug("Source %p: Start_Ind", sep);
 
 	if (!a2dp_sep->locked) {
 		a2dp_sep->session = avdtp_ref(session);
@@ -623,9 +624,9 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_setup *setup;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Start_Cfm");
+		debug("Sink %p: Start_Cfm", sep);
 	else
-		debug("SBC Source: Start_Cfm");
+		debug("Source %p: Start_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 	if (!setup)
@@ -654,9 +655,9 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Suspend_Ind");
+		debug("Sink %p: Suspend_Ind", sep);
 	else
-		debug("SBC Source: Suspend_Ind");
+		debug("Source %p: Suspend_Ind", sep);
 
 	if (a2dp_sep->suspend_timer) {
 		g_source_remove(a2dp_sep->suspend_timer);
@@ -677,9 +678,9 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	gboolean start;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Suspend_Cfm");
+		debug("Sink %p: Suspend_Cfm", sep);
 	else
-		debug("SBC Source: Suspend_Cfm");
+		debug("Source %p: Suspend_Cfm", sep);
 
 	a2dp_sep->suspending = FALSE;
 
@@ -720,9 +721,9 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Close_Ind");
+		debug("Sink %p: Close_Ind", sep);
 	else
-		debug("SBC Source: Close_Ind");
+		debug("Source %p: Close_Ind", sep);
 
 	return TRUE;
 }
@@ -762,9 +763,9 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_setup *setup;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Close_Cfm");
+		debug("Sink %p: Close_Cfm", sep);
 	else
-		debug("SBC Source: Close_Cfm");
+		debug("Source %p: Close_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 	if (!setup)
@@ -793,9 +794,9 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Abort_Ind");
+		debug("Sink %p: Abort_Ind", sep);
 	else
-		debug("SBC Source: Abort_Ind");
+		debug("Source %p: Abort_Ind", sep);
 
 	a2dp_sep->stream = NULL;
 
@@ -810,9 +811,9 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_setup *setup;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: Abort_Cfm");
+		debug("Sink %p: Abort_Cfm", sep);
 	else
-		debug("SBC Source: Abort_Cfm");
+		debug("Source %p: Abort_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 	if (!setup)
@@ -827,9 +828,9 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_sep *a2dp_sep = user_data;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: ReConfigure_Ind");
+		debug("Sink %p: ReConfigure_Ind", sep);
 	else
-		debug("SBC Source: ReConfigure_Ind");
+		debug("Source %p: ReConfigure_Ind", sep);
 	return TRUE;
 }
 
@@ -841,9 +842,9 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	struct a2dp_setup *setup;
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
-		debug("SBC Sink: ReConfigure_Cfm");
+		debug("Sink %p: ReConfigure_Cfm", sep);
 	else
-		debug("SBC Source: ReConfigure_Cfm");
+		debug("Source %p: ReConfigure_Cfm", sep);
 
 	setup = find_setup_by_session(session);
 	if (!setup)
@@ -1161,7 +1162,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,
 		return 0;
 	}
 
-	debug("a2dp_source_config: selected SEP %p", sep);
+	debug("a2dp_source_config: selected SEP %p", sep->sep);
 
 	cb_data = g_new0(struct a2dp_setup_cb, 1);
 	cb_data->config_cb = cb;
@@ -1337,7 +1338,7 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
 	if (sep->locked)
 		return FALSE;
 
-	debug("SBC Source SEP %p locked", sep);
+	debug("SEP %p locked", sep->sep);
 	sep->locked = TRUE;
 
 	return TRUE;
@@ -1351,7 +1352,7 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
 
 	sep->locked = FALSE;
 
-	debug("SBC Source SEP %p unlocked", sep);
+	debug("SEP %p unlocked", sep->sep);
 
 	if (!sep->stream || state == AVDTP_STATE_IDLE)
 		return TRUE;
-- 
1.5.3.7
0011-Bug-fixes-for-gstreamer-plugin.patch (text/x-diff, 9.3 KB)
From 887c340e06c248d55b0a77cbe3d7de5579b7cd3e Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <[email protected]>
Date: Mon, 21 Jan 2008 14:17:30 -0300
Subject: [PATCH] Bug fixes for gstreamer plugin.

---
 audio/gsta2dpsink.c |  194 ++++++++++++++++++++++++++++++---------------------
 audio/gsta2dpsink.h |    2 +-
 2 files changed, 114 insertions(+), 82 deletions(-)

diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index 091d547..7c68d17 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -69,6 +69,56 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
 static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
 static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
 
+/*
+ * Helper function to create elements, add to the bin and link it
+ * to another element.
+ */
+static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self,
+			const gchar* elementname, const gchar* name,
+			GstElement *link_to)
+{
+	GstElement *element;
+
+	GST_LOG_OBJECT(self, "Initializing %s", elementname);
+
+	element = gst_element_factory_make(elementname, name);
+	if (element == NULL) {
+		GST_ERROR_OBJECT(self, "Couldn't create %s", elementname);
+		return NULL;
+	}
+
+	if (!gst_bin_add(GST_BIN(self), element)) {
+		GST_ERROR_OBJECT(self, "failed to add %s to the bin",
+						elementname);
+		goto cleanup_and_fail;
+	}
+
+	if (gst_element_set_state(element, GST_STATE_READY) ==
+			GST_STATE_CHANGE_FAILURE) {
+		GST_ERROR_OBJECT(self, "%s failed to go to ready",
+						elementname);
+		goto remove_element_and_fail;
+	}
+
+	if (!gst_element_link(link_to, element)) {
+		GST_ERROR_OBJECT(self, "couldn't link %s", elementname);
+		goto remove_element_and_fail;
+	}
+
+	return element;
+
+remove_element_and_fail:
+	gst_element_set_state(element, GST_STATE_NULL);
+	gst_bin_remove(GST_BIN(self), element);
+	return NULL;
+
+cleanup_and_fail:
+	if (element != NULL)
+		g_object_unref(G_OBJECT(element));
+
+	return NULL;
+}
+
 static void gst_a2dp_sink_base_init(gpointer g_class)
 {
 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
@@ -153,6 +203,18 @@ static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
 	return TRUE;
 }
 
+static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
+{
+	if (self->rtp) {
+		GST_LOG_OBJECT(self, "removing rtp element from the bin");
+		if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
+			GST_WARNING_OBJECT(self, "failed to remove rtp "
+					"element from bin");
+		else
+			self->rtp = NULL;
+	}
+}
+
 static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 			GstStateChange transition)
 {
@@ -165,6 +227,8 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 		break;
 
 	case GST_STATE_CHANGE_NULL_TO_READY:
+		self->sink_is_in_bin = FALSE;
+
 		self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make(
 				"a2dpsendersink", "sendersink"));
 		if (self->sink == NULL) {
@@ -199,16 +263,24 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
 			gst_event_unref(self->newseg_event);
 			self->newseg_event = NULL;
 		}
-		if (self->taglist) {
-			gst_tag_list_free(self->taglist);
-			self->taglist = NULL;
-		}
 		break;
 
 	case GST_STATE_CHANGE_READY_TO_NULL:
-		if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink)))
-			GST_WARNING_OBJECT(self, "Failed to remove "
-					"a2dpsendersink from bin");
+		if (self->sink_is_in_bin) {
+			if (!gst_bin_remove(GST_BIN(self),
+						GST_ELEMENT(self->sink)))
+				GST_WARNING_OBJECT(self, "Failed to remove "
+						"a2dpsendersink from bin");
+		} else if (self->sink != NULL) {
+			gst_element_set_state(GST_ELEMENT(self->sink),
+					GST_STATE_NULL);
+			g_object_unref(G_OBJECT(self->sink));
+		}
+
+		self->sink = NULL;
+
+		gst_a2dp_sink_remove_dynamic_elements(self);
+
 		break;
 
 	default:
@@ -304,6 +376,7 @@ static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self)
 	}
 
 	self->sink = GST_A2DP_SENDER_SINK(sink);
+	self->sink_is_in_bin = TRUE;
 	g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
 
 	gst_element_set_state(sink, GST_STATE_PAUSED);
@@ -326,28 +399,10 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
 {
 	GstElement *rtppay;
 
-	rtppay = gst_element_factory_make("rtpsbcpay", "rtp");
-	if (rtppay == NULL) {
-		GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay");
+	rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
+						self->capsfilter);
+	if (rtppay == NULL)
 		return FALSE;
-	}
-
-	if (!gst_bin_add(GST_BIN(self), rtppay)) {
-		GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin");
-		goto cleanup_and_fail;
-	}
-
-	if (gst_element_set_state(rtppay, GST_STATE_READY) ==
-			GST_STATE_CHANGE_FAILURE) {
-		GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready");
-		goto remove_element_and_fail;
-	}
-
-	if (!gst_element_link(self->capsfilter, rtppay)) {
-		GST_ERROR_OBJECT(self, "couldn't link capsfilter "
-			"to rtpsbcpay");
-		goto remove_element_and_fail;
-	}
 
 	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
 	g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
@@ -355,86 +410,48 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
 	gst_element_set_state(rtppay, GST_STATE_PAUSED);
 
 	return TRUE;
-
-remove_element_and_fail:
-	gst_element_set_state(rtppay, GST_STATE_NULL);
-	gst_bin_remove(GST_BIN(self), rtppay);
-	return FALSE;
-
-cleanup_and_fail:
-	if (rtppay != NULL)
-		g_object_unref(G_OBJECT(rtppay));
-
-	return FALSE;
 }
 
 static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
 {
 	GstElement *rtppay;
 
-	/* FIXME we will need a internal mpegparse for identifying
-	 * stream stuff */
+	GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
 
-	rtppay = gst_element_factory_make("rtpmpapay", "rtp");
-	if (rtppay == NULL) {
-		GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay");
+	/* if capsfilter is not created then we can't have our rtp element */
+	if (self->capsfilter == NULL)
 		return FALSE;
-	}
 
-	if (!gst_bin_add(GST_BIN(self), rtppay)) {
-		GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin");
-		goto cleanup_and_fail;
-	}
-
-	if (gst_element_set_state(rtppay, GST_STATE_READY) ==
-			GST_STATE_CHANGE_FAILURE) {
-		GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready");
-		goto remove_element_and_fail;
-	}
-
-	if (!gst_element_link(self->capsfilter, rtppay)) {
-		GST_ERROR_OBJECT(self, "couldn't link capsfilter "
-			"to rtpmpapay");
-		goto remove_element_and_fail;
-	}
+	rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", 
+					self->capsfilter);
+	if (rtppay == NULL)
+		return FALSE;
 
 	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
 
 	gst_element_set_state(rtppay, GST_STATE_PAUSED);
 
 	return TRUE;
-
-remove_element_and_fail:
-	gst_element_set_state (rtppay, GST_STATE_NULL);
-	gst_bin_remove(GST_BIN(self), rtppay);
-	return FALSE;
-
-cleanup_and_fail:
-	if (rtppay != NULL)
-		g_object_unref(G_OBJECT(rtppay));
-
-	return FALSE;
 }
 
-static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
+static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
+						GstCaps *caps)
 {
-	GstA2dpSink *self;
 	GstStructure *structure;
 	GstEvent *event;
 	GstPad *capsfilterpad;
 	gboolean crc;
 	gchar *mode = NULL;
 
-	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
-	GST_INFO_OBJECT(self, "setting caps");
-
 	structure = gst_caps_get_structure(caps, 0);
 
 	/* first, we need to create our rtp payloader */
 	if (gst_structure_has_name(structure, "audio/x-sbc")) {
+		GST_LOG_OBJECT(self, "sbc media received");
 		if (!gst_a2dp_sink_init_rtp_sbc_element(self))
 			return FALSE;
 	} else if (gst_structure_has_name(structure, "audio/mpeg")) {
+		GST_LOG_OBJECT(self, "mp3 media received");
 		if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
 			return FALSE;
 	} else {
@@ -473,9 +490,24 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
 		gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL);
 
 	/* we forward our new segment here if we have one */
-	gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
-				self->newseg_event);
-	self->newseg_event = NULL;
+	if (self->newseg_event) {
+		gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
+					self->newseg_event);
+		self->newseg_event = NULL;
+	}
+
+	return TRUE;
+}
+
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
+{
+	GstA2dpSink *self;
+
+	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+	GST_INFO_OBJECT(self, "setting caps");
+
+	/* now we know the caps */
+	gst_a2dp_sink_init_dynamic_elements(self, caps);
 
 	return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
 }
@@ -537,10 +569,10 @@ static void gst_a2dp_sink_init(GstA2dpSink *self,
 	self->rtp = NULL;
 	self->device = NULL;
 	self->capsfilter = NULL;
-	self->mpegparse = NULL;
 	self->newseg_event = NULL;
 	self->taglist = NULL;
 	self->ghostpad = NULL;
+	self->sink_is_in_bin = FALSE;
 
 	/* we initialize our capsfilter */
 	gst_a2dp_sink_init_caps_filter(self);
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index f74185e..368f837 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -47,9 +47,9 @@ struct _GstA2dpSink {
 	GstBaseRTPPayload *rtp;
 	GstA2dpSenderSink *sink;
 	GstElement *capsfilter;
-	GstElement *mpegparse;
 
 	gchar *device;
+	gboolean sink_is_in_bin;
 
 	GstGhostPad *ghostpad;
 	GstPadSetCapsFunction ghostpad_setcapsfunc;
-- 
1.5.3.7
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.