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