Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
rb-player-gst-helper.c:124:37 | clang-analyzer | Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'element') | ||
rb-player-gst-helper.c:124:37 | clang-analyzer | Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'element') |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #include <config.h>
30
31 #include <string.h>
32
33 #include <gst/gst.h>
34 #include <gst/tag/tag.h>
35
36 #include <rb-player-gst-helper.h>
37 #include <rb-player-gst-filter.h>
38 #include <rb-player-gst-tee.h>
39 #include <rb-debug.h>
40
41 /* data structure for pipeline block-add/remove-unblock operations */
42 typedef struct {
43 GObject *player;
44 GstElement *element;
45 GstElement *fixture;
46 } RBGstPipelineOp;
47
48 GstElement *
49 rb_player_gst_try_audio_sink (const char *plugin_name, const char *name)
50 {
51 GstElement *audio_sink;
52
53 audio_sink = gst_element_factory_make (plugin_name, name);
54 if (audio_sink == NULL)
55 return NULL;
56
57 /* Assume the fakesink will work */
58 if (g_str_equal (plugin_name, "fakesink")) {
59 g_object_set (audio_sink, "sync", TRUE, NULL);
60 return audio_sink;
61 }
62
63 if (audio_sink) {
64 GstStateChangeReturn ret;
65 GstBus *bus;
66
67 /* use the 'music and movies' profile for gconfaudiosink and gsettingsaudiosink */
68 if ((strcmp (plugin_name, "gconfaudiosink") == 0 ||
69 strcmp (plugin_name, "gsettingsaudiosink") == 0) &&
70 g_object_class_find_property (G_OBJECT_GET_CLASS (audio_sink), "profile")) {
71 rb_debug ("setting profile property on %s", plugin_name);
72 g_object_set (audio_sink, "profile", 1, NULL);
73 }
74
75 /* need to set bus explicitly as it's not in a bin yet and
76 * we need one to catch error messages */
77 bus = gst_bus_new ();
78 gst_element_set_bus (audio_sink, bus);
79
80 /* state change NULL => READY should always be synchronous */
81 ret = gst_element_set_state (audio_sink, GST_STATE_READY);
82 gst_element_set_bus (audio_sink, NULL);
83
84 if (ret == GST_STATE_CHANGE_FAILURE) {
85 /* doesn't work, drop this audio sink */
86 rb_debug ("audio sink %s failed to change to READY state", plugin_name);
87 gst_element_set_state (audio_sink, GST_STATE_NULL);
88 gst_object_unref (audio_sink);
89 audio_sink = NULL;
90 } else {
91 rb_debug ("audio sink %s changed to READY state successfully", plugin_name);
92 }
93 gst_object_unref (bus);
94 }
95
96 return audio_sink;
97 }
98
99 static gint
100 find_property_element (GstElement *element, const char *property)
101 {
102 gint res = 1;
103 char *name = gst_element_get_name (element);
104
105 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), property) != NULL) {
106 rb_debug ("found property \"%s\" on element %s", property, name);
107 return 0;
108 } else {
109 rb_debug ("didn't find property \"%s\" on element %s", property, name);
110 g_object_unref (element);
111 }
112
113 g_free (name);
114 return res;
115 }
116
117 GstElement *
118 rb_player_gst_find_element_with_property (GstElement *element, const char *property)
119 {
120 GstIterator *iter;
121 GstElement *result;
122
123 if (GST_IS_BIN (element) == FALSE) {
124 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
125 property) != NULL) {
126 return g_object_ref (element);
127 }
128 return NULL;
129 }
130
131 rb_debug ("iterating bin looking for property %s", property);
132 iter = gst_bin_iterate_recurse (GST_BIN (element));
133 result = gst_iterator_find_custom (iter,
134 (GCompareFunc) find_property_element,
135 (gpointer) property);
136 gst_iterator_free (iter);
137 return result;
138 }
139
140 /**
141 * rb_gst_process_embedded_image:
142 * @taglist: a #GstTagList containing an image
143 * @tag: the tag name
144 *
145 * Converts embedded image data extracted from a tag list into
146 * a #GdkPixbuf. The returned #GdkPixbuf is owned by the caller.
147 *
148 * Returns: a #GdkPixbuf, or NULL.
149 */
150 GdkPixbuf *
151 rb_gst_process_embedded_image (const GstTagList *taglist, const char *tag)
152 {
153 GstBuffer *buf;
154 GdkPixbufLoader *loader;
155 GdkPixbuf *pixbuf;
156 GError *error = NULL;
157 const GValue *val = NULL;
158 guint i;
159
160 for (i = 0; ; i++) {
161 const GValue *value;
162 const char *media_type;
163 GstStructure *caps_struct;
164 int imgtype;
165
166 value = gst_tag_list_get_value_index (taglist, tag, i);
167 if (value == NULL) {
168 break;
169 }
170
171 buf = gst_value_get_buffer (value);
172 if (buf == NULL) {
173 rb_debug ("apparently couldn't get image buffer");
174 continue;
175 }
176
177 caps_struct = gst_caps_get_structure (buf->caps, 0);
178
179 media_type = gst_structure_get_name (caps_struct);
180 /* other things to ignore? */
181 if (g_strcmp0 (media_type, "text/uri-list") == 0) {
182 rb_debug ("ignoring text/uri-list image tag");
183 continue;
184 }
185
186 gst_structure_get_enum (caps_struct, "image-type", GST_TYPE_TAG_IMAGE_TYPE, &imgtype);
187 rb_debug ("image type %d", imgtype);
188 if (imgtype == GST_TAG_IMAGE_TYPE_UNDEFINED) {
189 if (val == NULL) {
190 rb_debug ("got undefined image type");
191 val = value;
192 }
193 } else if (imgtype == GST_TAG_IMAGE_TYPE_FRONT_COVER) {
194 rb_debug ("got front cover image");
195 val = value;
196 }
197 }
198
199 if (val == NULL) {
200 rb_debug ("couldn't find an image to process");
201 return NULL;
202 }
203
204 rb_debug ("found image at value %u for tag %s", i, tag);
205
206 loader = gdk_pixbuf_loader_new ();
207 buf = gst_value_get_buffer (val);
208 rb_debug ("sending %d bytes to pixbuf loader", buf->size);
209 if (gdk_pixbuf_loader_write (loader, buf->data, buf->size, &error) == FALSE) {
210 rb_debug ("pixbuf loader doesn't like the data: %s", error->message);
211 g_error_free (error);
212 g_object_unref (loader);
213 return NULL;
214 }
215
216 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
217 if (pixbuf != NULL) {
218 g_object_ref (pixbuf);
219 }
220
221 gdk_pixbuf_loader_close (loader, NULL);
222 g_object_unref (loader);
223
224 if (pixbuf == NULL) {
225 rb_debug ("pixbuf loader didn't give us a pixbuf");
226 return NULL;
227 }
228
229 rb_debug ("returning embedded image: %d x %d / %d",
230 gdk_pixbuf_get_width (pixbuf),
231 gdk_pixbuf_get_height (pixbuf),
232 gdk_pixbuf_get_bits_per_sample (pixbuf));
233 return pixbuf;
234 }
235
236 /**
237 * rb_gst_process_tag_string:
238 * @taglist: a #GstTagList containing a string tag
239 * @tag: tag name
240 * @field: returns the #RBMetaDataField corresponding to the tag
241 * @value: returns the tag value
242 *
243 * Processes a tag string, determining the metadata field identifier
244 * corresponding to the tag name, and converting the tag data into the
245 * appropriate value type.
246 *
247 * Return value: %TRUE if the tag was successfully converted.
248 */
249 gboolean
250 rb_gst_process_tag_string (const GstTagList *taglist,
251 const char *tag,
252 RBMetaDataField *field,
253 GValue *value)
254 {
255 const GValue *tagval;
256
257 if (gst_tag_list_get_tag_size (taglist, tag) < 0) {
258 rb_debug ("no values in taglist for tag %s", tag);
259 return FALSE;
260 }
261
262 /* only handle a few fields here */
263 if (!strcmp (tag, GST_TAG_TITLE))
264 *field = RB_METADATA_FIELD_TITLE;
265 else if (!strcmp (tag, GST_TAG_GENRE))
266 *field = RB_METADATA_FIELD_GENRE;
267 else if (!strcmp (tag, GST_TAG_COMMENT))
268 *field = RB_METADATA_FIELD_COMMENT;
269 else if (!strcmp (tag, GST_TAG_BITRATE))
270 *field = RB_METADATA_FIELD_BITRATE;
271 else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID))
272 *field = RB_METADATA_FIELD_MUSICBRAINZ_TRACKID;
273 else {
274 rb_debug ("tag %s doesn't correspond to a metadata field we're interested in", tag);
275 return FALSE;
276 }
277
278 /* most of the fields we care about are strings */
279 switch (*field) {
280 case RB_METADATA_FIELD_BITRATE:
281 g_value_init (value, G_TYPE_ULONG);
282 break;
283
284 case RB_METADATA_FIELD_TITLE:
285 case RB_METADATA_FIELD_GENRE:
286 case RB_METADATA_FIELD_COMMENT:
287 case RB_METADATA_FIELD_MUSICBRAINZ_TRACKID:
288 default:
289 g_value_init (value, G_TYPE_STRING);
290 break;
291 }
292
293 tagval = gst_tag_list_get_value_index (taglist, tag, 0);
294 if (!g_value_transform (tagval, value)) {
295 rb_debug ("Could not transform tag value type %s into %s",
296 g_type_name (G_VALUE_TYPE (tagval)),
297 g_type_name (G_VALUE_TYPE (value)));
298 g_value_unset (value);
299 return FALSE;
300 }
301
302 return TRUE;
303 }
304
305 /**
306 * rb_gst_error_get_error_code:
307 * @error: error received from GStreamer
308 *
309 * Maps a GStreamer error to an #RBPlayerError error code.
310 *
311 * Return value: the #RBPlayerError value to use
312 */
313 int
314 rb_gst_error_get_error_code (const GError *error)
315 {
316 if (error->domain == GST_RESOURCE_ERROR &&
317 (error->code == GST_RESOURCE_ERROR_NOT_FOUND ||
318 error->code == GST_RESOURCE_ERROR_OPEN_READ ||
319 error->code == GST_RESOURCE_ERROR_READ)) {
320 return RB_PLAYER_ERROR_NOT_FOUND;
321 } else if ((error->domain == GST_CORE_ERROR)
322 || (error->domain == GST_LIBRARY_ERROR)
323 || (error->domain == GST_RESOURCE_ERROR && error->code == GST_RESOURCE_ERROR_BUSY)) {
324 return RB_PLAYER_ERROR_NO_AUDIO;
325 } else {
326 return RB_PLAYER_ERROR_GENERAL;
327 }
328 }
329
330 /* pipeline block-add/remove-unblock operations */
331
332 static RBGstPipelineOp *
333 new_pipeline_op (GObject *player, GstElement *fixture, GstElement *element)
334 {
335 RBGstPipelineOp *op;
336 op = g_new0 (RBGstPipelineOp, 1);
337 op->player = g_object_ref (player);
338 op->fixture = gst_object_ref (fixture);
339 op->element = gst_object_ref (element);
340 return op;
341 }
342
343 static void
344 free_pipeline_op (RBGstPipelineOp *op)
345 {
346 g_object_unref (op->player);
347 gst_object_unref (op->element);
348 gst_object_unref (op->fixture);
349 g_free (op);
350 }
351
352 static void
353 pipeline_op_done (GstPad *pad, gboolean blocked, GstPad *new_pad)
354 {
355 GstEvent *segment;
356 if (new_pad == NULL)
357 return;
358
359 /* send a very unimaginative new segment through the new pad */
360 segment = gst_event_new_new_segment (TRUE,
361 1.0,
362 GST_FORMAT_DEFAULT,
363 0,
364 GST_CLOCK_TIME_NONE,
365 0);
366 gst_pad_send_event (new_pad, segment);
367 gst_object_unref (new_pad);
368 }
369
370 static gboolean
371 pipeline_op (GObject *player,
372 GstElement *fixture,
373 GstElement *element,
374 gboolean use_pad_block,
375 GstPadBlockCallback callback)
376 {
377 RBGstPipelineOp *op;
378 GstPad *fixture_pad;
379 GstPad *block_pad;
380
381 op = new_pipeline_op (player, fixture, element);
382
383 /* seems like we should be able to just block the src pad connected
384 * to the fixture's sink pad..
385 */
386 fixture_pad = gst_element_get_static_pad (fixture, "sink");
387 block_pad = gst_pad_get_peer (fixture_pad);
388 gst_object_unref (fixture_pad);
389
390 if (use_pad_block) {
391 char *whatpad;
392 whatpad = gst_object_get_path_string (GST_OBJECT (block_pad));
393 rb_debug ("blocking pad %s to perform an operation", whatpad);
394 g_free (whatpad);
395
396 gst_pad_set_blocked_async (block_pad,
397 TRUE,
398 callback,
399 op);
400 } else {
401 rb_debug ("not using pad blocking, calling op directly");
402 (*callback) (block_pad, FALSE, op);
403 }
404
405 gst_object_unref (block_pad);
406 return TRUE;
407 }
408
409 /* RBPlayerGstFilter implementation */
410
411 /**
412 * rb_gst_create_filter_bin:
413 *
414 * Creates an initial bin to use for dynamically plugging filter elements into the
415 * pipeline.
416 *
417 * Return value: filter bin
418 */
419 GstElement *
420 rb_gst_create_filter_bin ()
421 {
422 GstElement *bin;
423 GstElement *audioconvert;
424 GstElement *identity;
425 GstPad *pad;
426
427 bin = gst_bin_new ("filterbin");
428
429 audioconvert = gst_element_factory_make ("audioconvert", "filteraudioconvert");
430 identity = gst_element_factory_make ("identity", "filteridentity");
431 gst_bin_add_many (GST_BIN (bin), audioconvert, identity, NULL);
432 gst_element_link (audioconvert, identity);
433
434 pad = gst_element_get_static_pad (audioconvert, "sink");
435 gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
436 gst_object_unref (pad);
437
438 pad = gst_element_get_static_pad (identity, "src");
439 gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
440 gst_object_unref (pad);
441
442 return bin;
443 }
444
445 static void
446 really_add_filter (GstPad *pad,
447 gboolean blocked,
448 RBGstPipelineOp *op)
449 {
450 GstPad *binsinkpad;
451 GstPad *binsrcpad;
452 GstPad *realpad;
453 GstPad *prevpad;
454 GstElement *bin;
455 GstElement *identity;
456 GstElement *audioconvert;
457 GstElement *audioconvert2;
458 GstPadLinkReturn link;
459
460 rb_debug ("adding filter %p", op->element);
461
462 /*
463 * it kind of looks like we need audioconvert elements on either side of each filter
464 * to prevent caps renegotiation from causing 'internal data flow error' errors.
465 * this probably means we'd be doing a few unnecessary conversions when there are
466 * multiple filters in the pipeline, but at least it works.
467 */
468
469 /* create containing bin */
470 bin = gst_bin_new (NULL);
471 audioconvert = gst_element_factory_make ("audioconvert", NULL);
472 audioconvert2 = gst_element_factory_make ("audioconvert", NULL);
473 gst_bin_add_many (GST_BIN (bin), audioconvert, op->element, audioconvert2, NULL);
474 gst_element_link_many (audioconvert, op->element, audioconvert2, NULL);
475
476 /* create ghost pads */
477 realpad = gst_element_get_static_pad (audioconvert, "sink");
478 binsinkpad = gst_ghost_pad_new ("sink", realpad);
479 gst_element_add_pad (bin, binsinkpad);
480 gst_object_unref (realpad);
481
482 realpad = gst_element_get_static_pad (audioconvert2, "src");
483 binsrcpad = gst_ghost_pad_new ("src", realpad);
484 gst_element_add_pad (bin, binsrcpad);
485 gst_object_unref (realpad);
486
487 /* chuck it into the filter bin */
488 gst_bin_add (GST_BIN (op->fixture), bin);
489 identity = gst_bin_get_by_name (GST_BIN (op->fixture), "filteridentity");
490 realpad = gst_element_get_static_pad (identity, "sink");
491 prevpad = gst_pad_get_peer (realpad);
492 gst_object_unref (identity);
493
494 gst_pad_unlink (prevpad, realpad);
495
496 link = gst_pad_link (prevpad, binsinkpad);
497 gst_object_unref (prevpad);
498 if (link != GST_PAD_LINK_OK) {
499 g_warning ("couldn't link new filter into pipeline (sink): %d", link);
500 /* make some attempt at cleaning up; probably won't work though */
501 gst_pad_link (prevpad, realpad);
502 gst_object_unref (realpad);
503 gst_bin_remove (GST_BIN (op->fixture), bin);
504 gst_object_unref (bin);
505
506 free_pipeline_op (op);
507 return;
508 }
509
510 link = gst_pad_link (binsrcpad, realpad);
511 gst_object_unref (realpad);
512 if (link != GST_PAD_LINK_OK) {
513 g_warning ("couldn't link new filter into pipeline (src): %d", link);
514 /* doubt we can do anything helpful here.. */
515 }
516
517 /* if we're supposed to be playing, unblock the sink */
518 if (blocked) {
519 rb_debug ("unblocking pad after adding filter");
520 gst_element_set_state (bin, GST_STATE_PLAYING);
521 gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback)pipeline_op_done, NULL);
522 } else {
523 gst_element_set_state (bin, GST_STATE_PAUSED);
524 }
525
526 _rb_player_gst_filter_emit_filter_inserted (RB_PLAYER_GST_FILTER (op->player), op->element);
527
528 free_pipeline_op (op);
529 }
530
531 static void
532 really_remove_filter (GstPad *pad,
533 gboolean blocked,
534 RBGstPipelineOp *op)
535 {
536 GstPad *mypad;
537 GstPad *prevpad, *nextpad;
538 GstElement *bin;
539
540 /* get the containing bin and remove it */
541 bin = GST_ELEMENT (gst_element_get_parent (op->element));
542 if (bin == NULL) {
543 return;
544 }
545
546 rb_debug ("removing filter %p", op->element);
547 _rb_player_gst_filter_emit_filter_pre_remove (RB_PLAYER_GST_FILTER (op->player), op->element);
548
549 /* probably check return? */
550 gst_element_set_state (bin, GST_STATE_NULL);
551
552 /* unlink our sink */
553 mypad = gst_element_get_static_pad (bin, "sink");
554 prevpad = gst_pad_get_peer (mypad);
555 gst_pad_unlink (prevpad, mypad);
556 gst_object_unref (mypad);
557
558 /* unlink our src */
559 mypad = gst_element_get_static_pad (bin, "src");
560 nextpad = gst_pad_get_peer (mypad);
561 gst_pad_unlink (mypad, nextpad);
562 gst_object_unref (mypad);
563
564 /* link previous and next pads */
565 gst_pad_link (prevpad, nextpad);
566
567 gst_object_unref (prevpad);
568 gst_object_unref (nextpad);
569
570 gst_bin_remove (GST_BIN (op->fixture), bin);
571 gst_object_unref (bin);
572
573 /* if we're supposed to be playing, unblock the sink */
574 if (blocked) {
575 rb_debug ("unblocking pad after removing filter");
576 gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback)pipeline_op_done, NULL);
577 }
578
579 free_pipeline_op (op);
580 }
581
582 /**
583 * rb_gst_add_filter:
584 * @player: player object (must implement @RBPlayerGstFilter interface)
585 * @filterbin: the filter bin
586 * @element: the filter to add
587 * @use_pad_block: if %TRUE, block the src pad connected to the filter bin
588 *
589 * Inserts a filter into the filter bin, using pad blocking (if requested) to
590 * avoid breaking the data flow. Pad blocking should be used when the pipeline
591 * is in PLAYING state, or when in PAUSED state where a streaming thread will
592 * be holding the stream lock for the filter bin.
593 */
594 gboolean
595 rb_gst_add_filter (RBPlayer *player, GstElement *filterbin, GstElement *element, gboolean use_pad_block)
596 {
597 return pipeline_op (G_OBJECT (player),
598 filterbin,
599 element,
600 use_pad_block,
601 (GstPadBlockCallback) really_add_filter);
602 }
603
604 /**
605 * rb_gst_remove_filter:
606 * @player: player object (must implement @RBPlayerGstFilter interface)
607 * @filterbin: the filter bin
608 * @element: the filter to remove
609 * @use_pad_block: if %TRUE, block the src pad connected to the filter bin
610 *
611 * Removes a filter from the filter bin, using pad blocking (if requested) to
612 * avoid breaking the data flow. Pad blocking should be used when the pipeline
613 * is in PLAYING state, or when in PAUSED state where a streaming thread will
614 * be holding the stream lock for the filter bin.
615 */
616 gboolean
617 rb_gst_remove_filter (RBPlayer *player, GstElement *filterbin, GstElement *element, gboolean use_pad_block)
618 {
619 return pipeline_op (G_OBJECT (player),
620 filterbin,
621 element,
622 use_pad_block,
623 (GstPadBlockCallback) really_remove_filter);
624 }
625
626 /* RBPlayerGstTee implementation */
627
628 static void
629 really_add_tee (GstPad *pad, gboolean blocked, RBGstPipelineOp *op)
630 {
631 GstElement *queue;
632 GstElement *audioconvert;
633 GstElement *bin;
634 GstElement *parent_bin;
635 GstPad *sinkpad;
636 GstPad *ghostpad;
637
638 rb_debug ("really adding tee %p", op->element);
639
640 /* set up containing bin */
641 bin = gst_bin_new (NULL);
642 queue = gst_element_factory_make ("queue", NULL);
643 audioconvert = gst_element_factory_make ("audioconvert", NULL);
644
645 /* The bin contains elements that change state asynchronously
646 * and not as part of a state change in the entire pipeline.
647 */
648 g_object_set (bin, "async-handling", TRUE, NULL);
649
650 g_object_set (queue, "max-size-buffers", 3, NULL);
651
652 gst_bin_add_many (GST_BIN (bin), queue, audioconvert, op->element, NULL);
653 gst_element_link_many (queue, audioconvert, op->element, NULL);
654
655 /* add ghost pad */
656 sinkpad = gst_element_get_static_pad (queue, "sink");
657 ghostpad = gst_ghost_pad_new ("sink", sinkpad);
658 gst_element_add_pad (bin, ghostpad);
659 gst_object_unref (sinkpad);
660
661 /* add it into the pipeline */
662 parent_bin = GST_ELEMENT_PARENT (op->fixture);
663 gst_bin_add (GST_BIN (parent_bin), bin);
664 gst_element_link (op->fixture, bin);
665
666 /* if we're supposed to be playing, unblock the sink */
667 if (blocked) {
668 rb_debug ("unblocking pad after adding tee");
669
670 gst_element_set_state (parent_bin, GST_STATE_PLAYING);
671 gst_object_ref (ghostpad);
672 gst_pad_set_blocked_async (pad,
673 FALSE,
674 (GstPadBlockCallback)pipeline_op_done,
675 ghostpad);
676 } else {
677 gst_element_set_state (bin, GST_STATE_PAUSED);
678 gst_object_ref (ghostpad);
679 pipeline_op_done (NULL, FALSE, ghostpad);
680 }
681
682 _rb_player_gst_tee_emit_tee_inserted (RB_PLAYER_GST_TEE (op->player), op->element);
683
684 free_pipeline_op (op);
685 }
686
687 static void
688 really_remove_tee (GstPad *pad, gboolean blocked, RBGstPipelineOp *op)
689 {
690 GstElement *bin;
691 GstElement *parent;
692
693 rb_debug ("really removing tee %p", op->element);
694
695 _rb_player_gst_tee_emit_tee_pre_remove (RB_PLAYER_GST_TEE (op->player), op->element);
696
697 /* find bin, remove everything */
698 bin = GST_ELEMENT_PARENT (op->element);
699 g_object_ref (bin);
700
701 parent = GST_ELEMENT_PARENT (bin);
702 gst_bin_remove (GST_BIN (parent), bin);
703
704 gst_element_set_state (bin, GST_STATE_NULL);
705 gst_bin_remove (GST_BIN (bin), op->element);
706 g_object_unref (bin);
707
708 /* if we're supposed to be playing, unblock the sink */
709 if (blocked) {
710 rb_debug ("unblocking pad after removing tee");
711 gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback)pipeline_op_done, NULL);
712 }
713
714 free_pipeline_op (op);
715 }
716
717 /**
718 * rb_gst_add_tee:
719 * @player: player object (must implement @RBPlayerGstTee interface)
720 * @tee: a tee element
721 * @element: the tee branch to add
722 * @use_pad_block: if %TRUE, block the src pad connected to the filter bin
723 *
724 * Attaches a branch to the tee, using pad blocking (if requested) to
725 * avoid breaking the data flow. Pad blocking should be used when the pipeline
726 * is in PLAYING state, or when in PAUSED state where a streaming thread will
727 * be holding the stream lock for the filter bin.
728 */
729 gboolean
730 rb_gst_add_tee (RBPlayer *player, GstElement *tee, GstElement *element, gboolean use_pad_block)
731 {
732 return pipeline_op (G_OBJECT (player),
733 tee,
734 element,
735 use_pad_block,
736 (GstPadBlockCallback) really_add_tee);
737 }
738
739 /**
740 * rb_gst_remove_tee:
741 * @player: player object (must implement @RBPlayerGstTee interface)
742 * @tee: a tee element
743 * @element: the tee branch to remove
744 * @use_pad_block: if %TRUE, block the src pad connected to the filter bin
745 *
746 * Removes a branch from the tee, using pad blocking (if requested) to
747 * avoid breaking the data flow. Pad blocking should be used when the pipeline
748 * is in PLAYING state, or when in PAUSED state where a streaming thread will
749 * be holding the stream lock for the filter bin.
750 */
751 gboolean
752 rb_gst_remove_tee (RBPlayer *player, GstElement *tee, GstElement *element, gboolean use_pad_block)
753 {
754 return pipeline_op (G_OBJECT (player),
755 tee,
756 element,
757 use_pad_block,
758 (GstPadBlockCallback) really_remove_tee);
759 }