Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Lesser General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2.1 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Lesser General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Lesser General Public
     19  *  License along with this library; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <unistd.h>
     29 #include <pthread.h>
     30 
     31 #include "gstpragma.h"
     32 #include "gsta2dpsink.h"
     33 
     34 GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
     35 #define GST_CAT_DEFAULT gst_a2dp_sink_debug
     36 
     37 #define A2DP_SBC_RTP_PAYLOAD_TYPE 1
     38 #define TEMPLATE_MAX_BITPOOL_STR "64"
     39 
     40 #define DEFAULT_AUTOCONNECT TRUE
     41 
     42 enum {
     43 	PROP_0,
     44 	PROP_DEVICE,
     45 	PROP_AUTOCONNECT,
     46 	PROP_TRANSPORT
     47 };
     48 
     49 GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
     50 
     51 static const GstElementDetails gst_a2dp_sink_details =
     52 	GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
     53 				"Sink/Audio",
     54 				"Plays audio to an A2DP device",
     55 				"Marcel Holtmann <marcel (at) holtmann.org>");
     56 
     57 static GstStaticPadTemplate gst_a2dp_sink_factory =
     58 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
     59 			GST_STATIC_CAPS("audio/x-sbc, "
     60 				"rate = (int) { 16000, 32000, 44100, 48000 }, "
     61 				"channels = (int) [ 1, 2 ], "
     62 				"mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
     63 				"blocks = (int) { 4, 8, 12, 16 }, "
     64 				"subbands = (int) { 4, 8 }, "
     65 				"allocation = (string) { \"snr\", \"loudness\" }, "
     66 				"bitpool = (int) [ 2, "
     67 				TEMPLATE_MAX_BITPOOL_STR " ]; "
     68 				"audio/mpeg"
     69 				));
     70 
     71 static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
     72 static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
     73 static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
     74 static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
     75 static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self);
     76 static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self);
     77 
     78 static void gst_a2dp_sink_finalize(GObject *obj)
     79 {
     80 	GstA2dpSink *self = GST_A2DP_SINK(obj);
     81 
     82 	g_mutex_free(self->cb_mutex);
     83 
     84 	G_OBJECT_CLASS(parent_class)->finalize(obj);
     85 }
     86 
     87 static GstState gst_a2dp_sink_get_state(GstA2dpSink *self)
     88 {
     89 	GstState current, pending;
     90 
     91 	gst_element_get_state(GST_ELEMENT(self), &current, &pending, 0);
     92 	if (pending == GST_STATE_VOID_PENDING)
     93 		return current;
     94 
     95 	return pending;
     96 }
     97 
     98 /*
     99  * Helper function to create elements, add to the bin and link it
    100  * to another element.
    101  */
    102 static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self,
    103 			const gchar *elementname, const gchar *name,
    104 			GstElement *link_to)
    105 {
    106 	GstElement *element;
    107 	GstState state;
    108 
    109 	GST_LOG_OBJECT(self, "Initializing %s", elementname);
    110 
    111 	element = gst_element_factory_make(elementname, name);
    112 	if (element == NULL) {
    113 		GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname);
    114 		return NULL;
    115 	}
    116 
    117 	if (!gst_bin_add(GST_BIN(self), element)) {
    118 		GST_DEBUG_OBJECT(self, "failed to add %s to the bin",
    119 						elementname);
    120 		goto cleanup_and_fail;
    121 	}
    122 
    123 	state = gst_a2dp_sink_get_state(self);
    124 	if (gst_element_set_state(element, state) ==
    125 			GST_STATE_CHANGE_FAILURE) {
    126 		GST_DEBUG_OBJECT(self, "%s failed to go to playing",
    127 						elementname);
    128 		goto remove_element_and_fail;
    129 	}
    130 
    131 	if (link_to != NULL)
    132 		if (!gst_element_link(link_to, element)) {
    133 			GST_DEBUG_OBJECT(self, "couldn't link %s",
    134 					elementname);
    135 			goto remove_element_and_fail;
    136 		}
    137 
    138 	return element;
    139 
    140 remove_element_and_fail:
    141 	gst_element_set_state(element, GST_STATE_NULL);
    142 	gst_bin_remove(GST_BIN(self), element);
    143 	return NULL;
    144 
    145 cleanup_and_fail:
    146 	g_object_unref(G_OBJECT(element));
    147 
    148 	return NULL;
    149 }
    150 
    151 static void gst_a2dp_sink_base_init(gpointer g_class)
    152 {
    153 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
    154 
    155 	gst_element_class_set_details(element_class,
    156 		&gst_a2dp_sink_details);
    157 	gst_element_class_add_pad_template(element_class,
    158 		gst_static_pad_template_get(&gst_a2dp_sink_factory));
    159 }
    160 
    161 static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
    162 					const GValue *value, GParamSpec *pspec)
    163 {
    164 	GstA2dpSink *self = GST_A2DP_SINK(object);
    165 
    166 	switch (prop_id) {
    167 	case PROP_DEVICE:
    168 		if (self->sink != NULL)
    169 			gst_avdtp_sink_set_device(self->sink,
    170 				g_value_get_string(value));
    171 
    172 		if (self->device != NULL)
    173 			g_free(self->device);
    174 		self->device = g_value_dup_string(value);
    175 		break;
    176 
    177 	case PROP_TRANSPORT:
    178 		if (self->sink != NULL)
    179 			gst_avdtp_sink_set_transport(self->sink,
    180 				g_value_get_string(value));
    181 
    182 		if (self->transport != NULL)
    183 			g_free(self->transport);
    184 		self->transport = g_value_dup_string(value);
    185 		break;
    186 
    187 	case PROP_AUTOCONNECT:
    188 		self->autoconnect = g_value_get_boolean(value);
    189 
    190 		if (self->sink != NULL)
    191 			g_object_set(G_OBJECT(self->sink), "auto-connect",
    192 					self->autoconnect, NULL);
    193 		break;
    194 
    195 	default:
    196 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    197 		break;
    198 	}
    199 }
    200 
    201 static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
    202 					GValue *value, GParamSpec *pspec)
    203 {
    204 	GstA2dpSink *self = GST_A2DP_SINK(object);
    205 	gchar *device, *transport;
    206 
    207 	switch (prop_id) {
    208 	case PROP_DEVICE:
    209 		if (self->sink != NULL) {
    210 			device = gst_avdtp_sink_get_device(self->sink);
    211 			if (device != NULL)
    212 				g_value_take_string(value, device);
    213 		}
    214 		break;
    215 	case PROP_AUTOCONNECT:
    216 		if (self->sink != NULL)
    217 			g_object_get(G_OBJECT(self->sink), "auto-connect",
    218 				&self->autoconnect, NULL);
    219 
    220 		g_value_set_boolean(value, self->autoconnect);
    221 		break;
    222 	case PROP_TRANSPORT:
    223 		if (self->sink != NULL) {
    224 			transport = gst_avdtp_sink_get_transport(self->sink);
    225 			if (transport != NULL)
    226 				g_value_take_string(value, transport);
    227 		}
    228 		break;
    229 	default:
    230 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    231 		break;
    232 	}
    233 }
    234 
    235 static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
    236 {
    237 	GstPad *capsfilter_pad;
    238 
    239 	/* we search for the capsfilter sinkpad */
    240 	capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
    241 
    242 	/* now we add a ghostpad */
    243 	self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
    244 		capsfilter_pad));
    245 	g_object_unref(capsfilter_pad);
    246 
    247 	/* the getcaps of our ghostpad must reflect the device caps */
    248 	gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
    249 				gst_a2dp_sink_get_caps);
    250 	self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
    251 	gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
    252 			GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
    253 
    254 	/* we need to handle events on our own and we also need the eventfunc
    255 	 * of the ghostpad for forwarding calls */
    256 	self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
    257 	gst_pad_set_event_function(GST_PAD(self->ghostpad),
    258 			gst_a2dp_sink_handle_event);
    259 
    260 	if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
    261 		GST_ERROR_OBJECT(self, "failed to add ghostpad");
    262 
    263 	return TRUE;
    264 }
    265 
    266 static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
    267 {
    268 	if (self->rtp) {
    269 		GST_LOG_OBJECT(self, "removing rtp element from the bin");
    270 		if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
    271 			GST_WARNING_OBJECT(self, "failed to remove rtp "
    272 					"element from bin");
    273 		else
    274 			self->rtp = NULL;
    275 	}
    276 }
    277 
    278 static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
    279 			GstStateChange transition)
    280 {
    281 	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
    282 	GstA2dpSink *self = GST_A2DP_SINK(element);
    283 
    284 	switch (transition) {
    285 	case GST_STATE_CHANGE_READY_TO_PAUSED:
    286 		self->taglist = gst_tag_list_new();
    287 
    288 		gst_a2dp_sink_init_fakesink(self);
    289 		break;
    290 
    291 	case GST_STATE_CHANGE_NULL_TO_READY:
    292 		self->sink_is_in_bin = FALSE;
    293 		self->sink = GST_AVDTP_SINK(gst_element_factory_make(
    294 				"avdtpsink", "avdtpsink"));
    295 		if (self->sink == NULL) {
    296 			GST_WARNING_OBJECT(self, "failed to create avdtpsink");
    297 			return GST_STATE_CHANGE_FAILURE;
    298 		}
    299 
    300 		if (self->device != NULL)
    301 			gst_avdtp_sink_set_device(self->sink,
    302 					self->device);
    303 
    304 		if (self->transport != NULL)
    305 			gst_avdtp_sink_set_transport(self->sink,
    306 					self->transport);
    307 
    308 		g_object_set(G_OBJECT(self->sink), "auto-connect",
    309 					self->autoconnect, NULL);
    310 
    311 		ret = gst_element_set_state(GST_ELEMENT(self->sink),
    312 			GST_STATE_READY);
    313 		break;
    314 	default:
    315 		break;
    316 	}
    317 
    318 	if (ret == GST_STATE_CHANGE_FAILURE)
    319 		return ret;
    320 
    321 	ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
    322 								transition);
    323 
    324 	switch (transition) {
    325 	case GST_STATE_CHANGE_PAUSED_TO_READY:
    326 		if (self->taglist) {
    327 			gst_tag_list_free(self->taglist);
    328 			self->taglist = NULL;
    329 		}
    330 		if (self->newseg_event != NULL) {
    331 			gst_event_unref(self->newseg_event);
    332 			self->newseg_event = NULL;
    333 		}
    334 		gst_a2dp_sink_remove_fakesink(self);
    335 		break;
    336 
    337 	case GST_STATE_CHANGE_READY_TO_NULL:
    338 		if (self->sink_is_in_bin) {
    339 			if (!gst_bin_remove(GST_BIN(self),
    340 						GST_ELEMENT(self->sink)))
    341 				GST_WARNING_OBJECT(self, "Failed to remove "
    342 						"avdtpsink from bin");
    343 		} else if (self->sink != NULL) {
    344 			gst_element_set_state(GST_ELEMENT(self->sink),
    345 					GST_STATE_NULL);
    346 			g_object_unref(G_OBJECT(self->sink));
    347 		}
    348 
    349 		self->sink = NULL;
    350 
    351 		gst_a2dp_sink_remove_dynamic_elements(self);
    352 		break;
    353 	default:
    354 		break;
    355 	}
    356 
    357 	return ret;
    358 }
    359 
    360 static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
    361 {
    362 	GObjectClass *object_class = G_OBJECT_CLASS(klass);
    363 	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
    364 
    365 	parent_class = g_type_class_peek_parent(klass);
    366 
    367 	object_class->set_property = GST_DEBUG_FUNCPTR(
    368 					gst_a2dp_sink_set_property);
    369 	object_class->get_property = GST_DEBUG_FUNCPTR(
    370 					gst_a2dp_sink_get_property);
    371 
    372 	object_class->finalize = GST_DEBUG_FUNCPTR(
    373 					gst_a2dp_sink_finalize);
    374 
    375 	element_class->change_state = GST_DEBUG_FUNCPTR(
    376 					gst_a2dp_sink_change_state);
    377 
    378 	g_object_class_install_property(object_class, PROP_DEVICE,
    379 			g_param_spec_string("device", "Device",
    380 			"Bluetooth remote device address",
    381 			NULL, G_PARAM_READWRITE));
    382 
    383 	g_object_class_install_property(object_class, PROP_AUTOCONNECT,
    384 			g_param_spec_boolean("auto-connect", "Auto-connect",
    385 			"Automatically attempt to connect to device",
    386 			DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
    387 
    388 	g_object_class_install_property(object_class, PROP_TRANSPORT,
    389 			g_param_spec_string("transport", "Transport",
    390 			"Use configured transport",
    391 			NULL, G_PARAM_READWRITE));
    392 
    393 	GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
    394 				"A2DP sink element");
    395 }
    396 
    397 GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
    398 {
    399 	return gst_avdtp_sink_get_device_caps(self->sink);
    400 }
    401 
    402 static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
    403 {
    404 	GstCaps *caps;
    405 	GstCaps *caps_aux;
    406 	GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
    407 
    408 	if (self->sink == NULL) {
    409 		GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
    410 			"returning template caps");
    411 		caps = gst_static_pad_template_get_caps(
    412 				&gst_a2dp_sink_factory);
    413 	} else {
    414 		GST_LOG_OBJECT(self, "Getting device caps");
    415 		caps = gst_a2dp_sink_get_device_caps(self);
    416 		if (caps == NULL)
    417 			caps = gst_static_pad_template_get_caps(
    418 					&gst_a2dp_sink_factory);
    419 	}
    420 	caps_aux = gst_caps_copy(caps);
    421 	g_object_set(self->capsfilter, "caps", caps_aux, NULL);
    422 	gst_caps_unref(caps_aux);
    423 	return caps;
    424 }
    425 
    426 static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
    427 {
    428 	GstElement *sink;
    429 
    430 	/* check if we don't need a new sink */
    431 	if (self->sink_is_in_bin)
    432 		return TRUE;
    433 
    434 	if (self->sink == NULL)
    435 		sink = gst_element_factory_make("avdtpsink", "avdtpsink");
    436 	else
    437 		sink = GST_ELEMENT(self->sink);
    438 
    439 	if (sink == NULL) {
    440 		GST_ERROR_OBJECT(self, "Couldn't create avdtpsink");
    441 		return FALSE;
    442 	}
    443 
    444 	if (!gst_bin_add(GST_BIN(self), sink)) {
    445 		GST_ERROR_OBJECT(self, "failed to add avdtpsink "
    446 			"to the bin");
    447 		goto cleanup_and_fail;
    448 	}
    449 
    450 	if (gst_element_set_state(sink, GST_STATE_READY) ==
    451 			GST_STATE_CHANGE_FAILURE) {
    452 		GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready");
    453 		goto remove_element_and_fail;
    454 	}
    455 
    456 	if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
    457 		GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
    458 			"to avdtpsink");
    459 		goto remove_element_and_fail;
    460 	}
    461 
    462 	self->sink = GST_AVDTP_SINK(sink);
    463 	self->sink_is_in_bin = TRUE;
    464 	g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
    465 	g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
    466 
    467 	gst_element_set_state(sink, GST_STATE_PAUSED);
    468 
    469 	return TRUE;
    470 
    471 remove_element_and_fail:
    472 	gst_element_set_state(sink, GST_STATE_NULL);
    473 	gst_bin_remove(GST_BIN(self), sink);
    474 	return FALSE;
    475 
    476 cleanup_and_fail:
    477 	if (sink != NULL)
    478 		g_object_unref(G_OBJECT(sink));
    479 
    480 	return FALSE;
    481 }
    482 
    483 static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
    484 {
    485 	GstElement *rtppay;
    486 
    487 	/* if we already have a rtp, we don't need a new one */
    488 	if (self->rtp != NULL)
    489 		return TRUE;
    490 
    491 	rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
    492 						self->capsfilter);
    493 	if (rtppay == NULL)
    494 		return FALSE;
    495 
    496 	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
    497 	g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
    498 
    499 	gst_element_set_state(rtppay, GST_STATE_PAUSED);
    500 
    501 	return TRUE;
    502 }
    503 
    504 static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
    505 {
    506 	GstElement *rtppay;
    507 
    508 	/* check if we don't need a new rtp */
    509 	if (self->rtp)
    510 		return TRUE;
    511 
    512 	GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
    513 	/* if capsfilter is not created then we can't have our rtp element */
    514 	if (self->capsfilter == NULL)
    515 		return FALSE;
    516 
    517 	rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp",
    518 					self->capsfilter);
    519 	if (rtppay == NULL)
    520 		return FALSE;
    521 
    522 	self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
    523 
    524 	gst_element_set_state(rtppay, GST_STATE_PAUSED);
    525 
    526 	return TRUE;
    527 }
    528 
    529 static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
    530 						GstCaps *caps)
    531 {
    532 	GstStructure *structure;
    533 	GstEvent *event;
    534 	GstPad *capsfilterpad;
    535 	gboolean crc;
    536 	gchar *mode = NULL;
    537 
    538 	structure = gst_caps_get_structure(caps, 0);
    539 
    540 	/* before everything we need to remove fakesink */
    541 	gst_a2dp_sink_remove_fakesink(self);
    542 
    543 	/* first, we need to create our rtp payloader */
    544 	if (gst_structure_has_name(structure, "audio/x-sbc")) {
    545 		GST_LOG_OBJECT(self, "sbc media received");
    546 		if (!gst_a2dp_sink_init_rtp_sbc_element(self))
    547 			return FALSE;
    548 	} else if (gst_structure_has_name(structure, "audio/mpeg")) {
    549 		GST_LOG_OBJECT(self, "mp3 media received");
    550 		if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
    551 			return FALSE;
    552 	} else {
    553 		GST_ERROR_OBJECT(self, "Unexpected media type");
    554 		return FALSE;
    555 	}
    556 
    557 	if (!gst_a2dp_sink_init_avdtp_sink(self))
    558 		return FALSE;
    559 
    560 	/* check if we should push the taglist FIXME should we push this?
    561 	 * we can send the tags directly if needed */
    562 	if (self->taglist != NULL &&
    563 			gst_structure_has_name(structure, "audio/mpeg")) {
    564 
    565 		event = gst_event_new_tag(self->taglist);
    566 
    567 		/* send directly the crc */
    568 		if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
    569 			gst_avdtp_sink_set_crc(self->sink, crc);
    570 
    571 		if (gst_tag_list_get_string(self->taglist, "channel-mode",
    572 				&mode))
    573 			gst_avdtp_sink_set_channel_mode(self->sink, mode);
    574 
    575 		capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
    576 		gst_pad_send_event(capsfilterpad, event);
    577 		self->taglist = NULL;
    578 		g_free(mode);
    579 	}
    580 
    581 	if (!gst_avdtp_sink_set_device_caps(self->sink, caps))
    582 		return FALSE;
    583 
    584 	g_object_set(G_OBJECT(self->rtp), "mtu",
    585 		gst_avdtp_sink_get_link_mtu(self->sink), NULL);
    586 
    587 	/* we forward our new segment here if we have one */
    588 	if (self->newseg_event) {
    589 		gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
    590 					self->newseg_event);
    591 		self->newseg_event = NULL;
    592 	}
    593 
    594 	return TRUE;
    595 }
    596 
    597 static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
    598 {
    599 	GstA2dpSink *self;
    600 
    601 	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
    602 	GST_INFO_OBJECT(self, "setting caps");
    603 
    604 	/* now we know the caps */
    605 	gst_a2dp_sink_init_dynamic_elements(self, caps);
    606 
    607 	return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
    608 }
    609 
    610 /* used for catching newsegment events while we don't have a sink, for
    611  * later forwarding it to the sink */
    612 static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
    613 {
    614 	GstA2dpSink *self;
    615 	GstTagList *taglist = NULL;
    616 	GstObject *parent;
    617 
    618 	self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
    619 	parent = gst_element_get_parent(GST_ELEMENT(self->sink));
    620 
    621 	if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
    622 			parent != GST_OBJECT_CAST(self)) {
    623 		if (self->newseg_event != NULL)
    624 			gst_event_unref(self->newseg_event);
    625 		self->newseg_event = gst_event_ref(event);
    626 
    627 	} else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
    628 			parent != GST_OBJECT_CAST(self)) {
    629 		if (self->taglist == NULL)
    630 			gst_event_parse_tag(event, &self->taglist);
    631 		else {
    632 			gst_event_parse_tag(event, &taglist);
    633 			gst_tag_list_insert(self->taglist, taglist,
    634 					GST_TAG_MERGE_REPLACE);
    635 		}
    636 	}
    637 
    638 	if (parent != NULL)
    639 		gst_object_unref(GST_OBJECT(parent));
    640 
    641 	return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
    642 }
    643 
    644 static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
    645 {
    646 	GstElement *element;
    647 
    648 	element = gst_element_factory_make("capsfilter", "filter");
    649 	if (element == NULL)
    650 		goto failed;
    651 
    652 	if (!gst_bin_add(GST_BIN(self), element))
    653 		goto failed;
    654 
    655 	self->capsfilter = element;
    656 	return TRUE;
    657 
    658 failed:
    659 	GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
    660 	return FALSE;
    661 }
    662 
    663 static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self)
    664 {
    665 	if (self->fakesink != NULL)
    666 		return TRUE;
    667 
    668 	g_mutex_lock(self->cb_mutex);
    669 	self->fakesink = gst_a2dp_sink_init_element(self, "fakesink",
    670 			"fakesink", self->capsfilter);
    671 	g_mutex_unlock(self->cb_mutex);
    672 
    673 	if (!self->fakesink)
    674 		return FALSE;
    675 
    676 	return TRUE;
    677 }
    678 
    679 static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self)
    680 {
    681 	g_mutex_lock(self->cb_mutex);
    682 
    683 	if (self->fakesink != NULL) {
    684 		gst_element_set_locked_state(self->fakesink, TRUE);
    685 		gst_element_set_state(self->fakesink, GST_STATE_NULL);
    686 
    687 		gst_bin_remove(GST_BIN(self), self->fakesink);
    688 		self->fakesink = NULL;
    689 	}
    690 
    691 	g_mutex_unlock(self->cb_mutex);
    692 
    693 	return TRUE;
    694 }
    695 
    696 static void gst_a2dp_sink_init(GstA2dpSink *self,
    697 			GstA2dpSinkClass *klass)
    698 {
    699 	self->sink = NULL;
    700 	self->fakesink = NULL;
    701 	self->rtp = NULL;
    702 	self->device = NULL;
    703 	self->transport = NULL;
    704 	self->autoconnect = DEFAULT_AUTOCONNECT;
    705 	self->capsfilter = NULL;
    706 	self->newseg_event = NULL;
    707 	self->taglist = NULL;
    708 	self->ghostpad = NULL;
    709 	self->sink_is_in_bin = FALSE;
    710 
    711 	self->cb_mutex = g_mutex_new();
    712 
    713 	/* we initialize our capsfilter */
    714 	gst_a2dp_sink_init_caps_filter(self);
    715 	g_object_set(self->capsfilter, "caps",
    716 		gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
    717 		NULL);
    718 
    719 	gst_a2dp_sink_init_fakesink(self);
    720 
    721 	gst_a2dp_sink_init_ghost_pad(self);
    722 
    723 }
    724 
    725 gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin)
    726 {
    727 	return gst_element_register(plugin, "a2dpsink",
    728 			GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
    729 }
    730 
    731