hythmbox-2.98/backends/gstreamer/rb-player-gst-helper.c

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),
Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'element')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'element')
(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 }