~ubuntu-branches/ubuntu/intrepid/bluez/intrepid

« back to all changes in this revision

Viewing changes to audio/gsta2dpsink.c

  • Committer: Bazaar Package Importer
  • Author(s): Mario Limonciello
  • Date: 2008-10-07 12:10:29 UTC
  • Revision ID: james.westby@ubuntu.com-20081007121029-4gup4fmmh2vfo5nh
Tags: upstream-4.12
ImportĀ upstreamĀ versionĀ 4.12

Show diffs side-by-side

added added

removed removed

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