static GstPad * _get_peer_pad (GstPad * pad) { GstPad *peer = gst_pad_get_peer (pad); if (!peer) return NULL; while (GST_IS_PROXY_PAD (peer)) { GstPad *next_pad; if (GST_IS_GHOST_PAD (peer)) { next_pad = gst_pad_get_peer (peer); if (next_pad == pad) next_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer)); } else { next_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (peer))); } if (!next_pad) return NULL; gst_object_unref (peer); peer = next_pad; } return peer; }
/** * gst_ghost_pad_set_target: * @gpad: the #GstGhostPad * @newtarget: (transfer none) (allow-none): the new pad target * * Set the new target of the ghostpad @gpad. Any existing target * is unlinked and links to the new target are established. if @newtarget is * %NULL the target will be cleared. * * Returns: (transfer full): %TRUE if the new target could be set. This function * can return %FALSE when the internal pads could not be linked. */ gboolean gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget) { GstPad *internal; GstPad *oldtarget; GstPadLinkReturn lret; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE); g_return_val_if_fail (GST_PAD_CAST (gpad) != newtarget, FALSE); g_return_val_if_fail (newtarget != GST_PROXY_PAD_INTERNAL (gpad), FALSE); GST_OBJECT_LOCK (gpad); internal = GST_PROXY_PAD_INTERNAL (gpad); if (newtarget) GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget)); else GST_DEBUG_OBJECT (gpad, "clearing target"); /* clear old target */ if ((oldtarget = gst_pad_get_peer (internal))) { GST_OBJECT_UNLOCK (gpad); /* unlink internal pad */ if (GST_PAD_IS_SRC (internal)) gst_pad_unlink (internal, oldtarget); else gst_pad_unlink (oldtarget, internal); gst_object_unref (oldtarget); } else { GST_OBJECT_UNLOCK (gpad); } if (newtarget) { /* and link to internal pad without any checks */ GST_DEBUG_OBJECT (gpad, "connecting internal pad to target %" GST_PTR_FORMAT, newtarget); if (GST_PAD_IS_SRC (internal)) lret = gst_pad_link_full (internal, newtarget, GST_PAD_LINK_CHECK_NOTHING); else lret = gst_pad_link_full (newtarget, internal, GST_PAD_LINK_CHECK_NOTHING); if (lret != GST_PAD_LINK_OK) goto link_failed; } return TRUE; /* ERRORS */ link_failed: { GST_WARNING_OBJECT (gpad, "could not link internal and target, reason:%s", gst_pad_link_get_name (lret)); return FALSE; } }
static void debug_dump_element_pad (GstPad * pad, GstElement * element, GstDebugGraphDetails details, FILE * out, const gint indent) { GstElement *target_element; GstPad *target_pad, *tmp_pad; GstPadDirection dir; gchar *element_name; gchar *target_element_name; const gchar *color_name; dir = gst_pad_get_direction (pad); element_name = debug_dump_make_object_name (GST_OBJECT (element)); if (GST_IS_GHOST_PAD (pad)) { color_name = (dir == GST_PAD_SRC) ? "#ffdddd" : ((dir == GST_PAD_SINK) ? "#ddddff" : "#ffffff"); /* output target-pad so that it belongs to this element */ if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) { if ((target_pad = gst_pad_get_peer (tmp_pad))) { gchar *pad_name, *target_pad_name; const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)]; if ((target_element = gst_pad_get_parent_element (target_pad))) { target_element_name = debug_dump_make_object_name (GST_OBJECT (target_element)); } else { target_element_name = g_strdup (""); } debug_dump_pad (target_pad, color_name, target_element_name, details, out, indent); /* src ghostpad relationship */ pad_name = debug_dump_make_object_name (GST_OBJECT (pad)); target_pad_name = debug_dump_make_object_name (GST_OBJECT (target_pad)); if (dir == GST_PAD_SRC) { fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc, target_element_name, target_pad_name, element_name, pad_name); } else { fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc, element_name, pad_name, target_element_name, target_pad_name); } g_free (target_pad_name); g_free (target_element_name); if (target_element) gst_object_unref (target_element); gst_object_unref (target_pad); g_free (pad_name); } gst_object_unref (tmp_pad); } } else { color_name = (dir == GST_PAD_SRC) ? "#ffaaaa" : ((dir == GST_PAD_SINK) ? "#aaaaff" : "#cccccc"); } /* pads */ debug_dump_pad (pad, color_name, element_name, details, out, indent); g_free (element_name); }
static void log_new_pad_stats (GstPadStats * stats, GstPad * pad) { gst_tracer_record_log (tr_new_pad, (guint64) (guintptr) g_thread_self (), stats->index, stats->parent_ix, GST_OBJECT_NAME (pad), G_OBJECT_TYPE_NAME (pad), GST_IS_GHOST_PAD (pad), GST_PAD_DIRECTION (pad)); }
EXPORT_C #endif gboolean gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget) { GstPad *internal; GstPad *oldtarget; gboolean result; GstPadLinkReturn lret; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE); GST_PROXY_LOCK (gpad); internal = GST_PROXY_PAD_INTERNAL (gpad); GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget)); /* clear old target */ if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) { /* if we have an internal pad, unlink */ if (internal) { if (GST_PAD_IS_SRC (internal)) gst_pad_unlink (internal, oldtarget); else gst_pad_unlink (oldtarget, internal); } } result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget); if (result && newtarget) { /* and link to internal pad */ GST_DEBUG_OBJECT (gpad, "connecting internal pad to target"); if (GST_PAD_IS_SRC (internal)) lret = gst_pad_link (internal, newtarget); else lret = gst_pad_link (newtarget, internal); if (lret != GST_PAD_LINK_OK) goto link_failed; } GST_PROXY_UNLOCK (gpad); return result; /* ERRORS */ link_failed: { GST_WARNING_OBJECT (gpad, "could not link internal and target, reason:%d", lret); /* and unset target again */ gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), NULL); GST_PROXY_UNLOCK (gpad); return FALSE; } }
/** * gst_ghost_pad_get_target: * @gpad: the #GstGhostPad * * Get the target pad of @gpad. Unref target pad after usage. * * Returns: (transfer full): the target #GstPad, can be NULL if the ghostpad * has no target set. Unref target pad after usage. */ GstPad * gst_ghost_pad_get_target (GstGhostPad * gpad) { GstPad *ret; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL); ret = gst_proxy_pad_get_target (GST_PAD_CAST (gpad)); GST_DEBUG_OBJECT (gpad, "get target %s:%s", GST_DEBUG_PAD_NAME (ret)); return ret; }
static void log_new_pad_stats (GstPadStats * stats, GstPad * pad) { gst_tracer_log_trace (gst_structure_new ("new-pad", "thread-id", G_TYPE_UINT, GPOINTER_TO_UINT (g_thread_self ()), "ix", G_TYPE_UINT, stats->index, "parent-ix", G_TYPE_UINT, stats->parent_ix, "name", G_TYPE_STRING, GST_OBJECT_NAME (pad), "type", G_TYPE_STRING, G_OBJECT_TYPE_NAME (pad), "is-ghostpad", G_TYPE_BOOLEAN, GST_IS_GHOST_PAD (pad), "pad-direction", GST_TYPE_PAD_DIRECTION, GST_PAD_DIRECTION (pad), NULL)); }
/* TODO(ensonic): gst_pad_get_parent_element() would not work here, should we * add this as new api, e.g. gst_pad_find_parent_element(); */ static GstElement * get_real_pad_parent (GstPad * pad) { GstObject *parent; if (!pad) return NULL; parent = GST_OBJECT_PARENT (pad); /* if parent of pad is a ghost-pad, then pad is a proxy_pad */ if (parent && GST_IS_GHOST_PAD (parent)) { pad = GST_PAD_CAST (parent); parent = GST_OBJECT_PARENT (pad); } return GST_ELEMENT_CAST (parent); }
static gboolean gst_ghost_pad_activate_push_default (GstPad * pad, GstObject * parent, gboolean active) { gboolean ret; GstPad *other; g_return_val_if_fail (GST_IS_GHOST_PAD (pad), FALSE); GST_LOG_OBJECT (pad, "%sactivate push on %s:%s, proxy internal", (active ? "" : "de"), GST_DEBUG_PAD_NAME (pad)); /* just activate the internal pad */ other = GST_PROXY_PAD_INTERNAL (pad); ret = gst_pad_activate_mode (other, GST_PAD_MODE_PUSH, active); return ret; }
/** * gst_ghost_pad_activate_mode_default: * @pad: the #GstPad to activate or deactivate. * @parent: the parent of @pad or NULL * @mode: the requested activation mode * @active: whether the pad should be active or not. * * Invoke the default activate mode function of a ghost pad. * * Returns: %TRUE if the operation was successful. */ gboolean gst_ghost_pad_activate_mode_default (GstPad * pad, GstObject * parent, GstPadMode mode, gboolean active) { gboolean res; g_return_val_if_fail (GST_IS_GHOST_PAD (pad), FALSE); switch (mode) { case GST_PAD_MODE_PULL: res = gst_ghost_pad_activate_pull_default (pad, parent, active); break; case GST_PAD_MODE_PUSH: res = gst_ghost_pad_activate_push_default (pad, parent, active); break; default: GST_LOG_OBJECT (pad, "unknown activation mode %d", mode); res = FALSE; break; } return res; }
void test_parse_bin_from_description() { struct { const gchar *bin_desc; const gchar *pad_names; } bin_tests[] = { { "identity", "identity0/sink,identity0/src"}, { "identity ! identity ! identity", "identity1/sink,identity3/src"}, { "identity ! fakesink", "identity4/sink"}, { "fakesrc ! identity", "identity5/src"}, { "fakesrc ! fakesink", ""} }; gint i; xmlfile = "gstutils_test_parse_bin_from_description"; std_log(LOG_FILENAME_LINE, "Test Started gstutils_test_parse_bin_from_description"); for (i = 0; i < G_N_ELEMENTS (bin_tests); ++i) { GstElement *bin, *parent; GString *s; GstPad *ghost_pad, *target_pad; GError *err = NULL; bin = gst_parse_bin_from_description (bin_tests[i].bin_desc, TRUE, &err); if (err) { g_error ("ERROR in gst_parse_bin_from_description (%s): %s", bin_tests[i].bin_desc, err->message); } g_assert (bin != NULL); s = g_string_new (""); if ((ghost_pad = gst_element_get_pad (bin, "sink"))) { g_assert (GST_IS_GHOST_PAD (ghost_pad)); target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost_pad)); g_assert (target_pad != NULL); g_assert (GST_IS_PAD (target_pad)); parent = gst_pad_get_parent_element (target_pad); g_assert (parent != NULL); g_string_append_printf (s, "%s/sink", GST_ELEMENT_NAME (parent)); gst_object_unref (parent); gst_object_unref (target_pad); gst_object_unref (ghost_pad); } if ((ghost_pad = gst_element_get_pad (bin, "src"))) { g_assert (GST_IS_GHOST_PAD (ghost_pad)); target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost_pad)); g_assert (target_pad != NULL); g_assert (GST_IS_PAD (target_pad)); parent = gst_pad_get_parent_element (target_pad); g_assert (parent != NULL); if (s->len > 0) { g_string_append (s, ","); } g_string_append_printf (s, "%s/src", GST_ELEMENT_NAME (parent)); gst_object_unref (parent); gst_object_unref (target_pad); gst_object_unref (ghost_pad); } if (strcmp (s->str, bin_tests[i].pad_names) != 0) { g_error ("FAILED: expted '%s', got '%s' for bin '%s'", bin_tests[i].pad_names, s->str, bin_tests[i].bin_desc); } g_string_free (s, TRUE); gst_object_unref (bin); } std_log(LOG_FILENAME_LINE, "Test Successful"); create_xml(0); }
static void do_element_stats (GstStatsTracer * self, GstPad * pad, GstClockTime elapsed1, GstClockTime elapsed2) { GstClockTimeDiff elapsed = GST_CLOCK_DIFF (elapsed1, elapsed2); GstObject *parent = GST_OBJECT_PARENT (pad); GstElement *this = GST_ELEMENT_CAST (GST_IS_PAD (parent) ? GST_OBJECT_PARENT (parent) : parent); GstElementStats *this_stats = get_element_stats (self, this); GstPad *peer_pad = GST_PAD_PEER (pad); GstElementStats *peer_stats; if (!peer_pad) return; /* walk the ghost pad chain downstream to get the real pad */ /* if parent of peer_pad is a ghost-pad, then peer_pad is a proxy_pad */ parent = GST_OBJECT_PARENT (peer_pad); if (parent && GST_IS_GHOST_PAD (parent)) { peer_pad = GST_PAD_CAST (parent); /* if this is now the ghost pad, get the peer of this */ get_pad_stats (self, peer_pad); if ((parent = GST_OBJECT_PARENT (peer_pad))) { get_element_stats (self, GST_ELEMENT_CAST (parent)); } peer_pad = GST_PAD_PEER (GST_GHOST_PAD_CAST (peer_pad)); parent = peer_pad ? GST_OBJECT_PARENT (peer_pad) : NULL; } /* walk the ghost pad chain upstream to get the real pad */ /* if peer_pad is a ghost-pad, then parent is a bin and it is the parent of * a proxy_pad */ while (peer_pad && GST_IS_GHOST_PAD (peer_pad)) { get_pad_stats (self, peer_pad); get_element_stats (self, GST_ELEMENT_CAST (parent)); peer_pad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (peer_pad)); parent = peer_pad ? GST_OBJECT_PARENT (peer_pad) : NULL; } if (!parent) { printf ("%" GST_TIME_FORMAT " transmission on unparented target pad %s_%s -> %s_%s\n", GST_TIME_ARGS (elapsed), GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer_pad)); return; } peer_stats = get_element_stats (self, GST_ELEMENT_CAST (parent)); /* we'd like to gather time spend in each element, but this does not make too * much sense yet * pure push/pull-based: * - the time spend in the push/pull_range is accounted for the peer and * removed from the current element * - this works for chains * - drawback is sink elements that block to sync have a high time usage * - we could rerun the ests with sync=false * both: * - a.g. demuxers both push and pull. thus we subtract time for the pull * and the push operations, but never add anything. * - can we start a counter after push/pull in such elements and add then * time to the element upon next pad activity? */ #if 1 /* this does not make sense for demuxers */ this_stats->treal -= elapsed; peer_stats->treal += elapsed; #else /* this creates several >100% figures */ this_stats->treal += GST_CLOCK_DIFF (this_stats->last_ts, elapsed2) - elapsed; peer_stats->treal += elapsed; this_stats->last_ts = elapsed2; peer_stats->last_ts = elapsed2; #endif }
/* * debug_dump_element: * @bin: the bin that should be analyzed * @out: file to write to * @indent: level of graph indentation * * Helper for gst_debug_bin_to_dot_file() to recursively dump a pipeline. */ static void debug_dump_element (GstBin * bin, GstDebugGraphDetails details, GString * str, const gint indent) { GstIterator *element_iter, *pad_iter; gboolean elements_done, pads_done; GValue item = { 0, }; GValue item2 = { 0, }; GstElement *element; GstPad *pad = NULL; guint src_pads, sink_pads; gchar *element_name; gchar *state_name = NULL; gchar *param_name = NULL; const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)]; element_iter = gst_bin_iterate_elements (bin); elements_done = FALSE; while (!elements_done) { switch (gst_iterator_next (element_iter, &item)) { case GST_ITERATOR_OK: element = g_value_get_object (&item); element_name = debug_dump_make_object_name (GST_OBJECT (element)); if (details & GST_DEBUG_GRAPH_SHOW_STATES) { state_name = debug_dump_get_element_state (GST_ELEMENT (element)); } if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) { param_name = debug_dump_get_element_params (GST_ELEMENT (element), details); } /* elements */ g_string_append_printf (str, "%ssubgraph cluster_%s {\n", spc, element_name); g_string_append_printf (str, "%s fontname=\"Bitstream Vera Sans\";\n", spc); g_string_append_printf (str, "%s fontsize=\"8\";\n", spc); g_string_append_printf (str, "%s style=filled;\n", spc); g_string_append_printf (str, "%s color=black;\n\n", spc); g_string_append_printf (str, "%s label=\"%s\\n%s%s%s\";\n", spc, G_OBJECT_TYPE_NAME (element), GST_OBJECT_NAME (element), (state_name ? state_name : ""), (param_name ? param_name : "") ); if (state_name) { g_free (state_name); state_name = NULL; } if (param_name) { g_free (param_name); param_name = NULL; } g_free (element_name); src_pads = sink_pads = 0; if ((pad_iter = gst_element_iterate_sink_pads (element))) { debug_dump_element_pads (pad_iter, pad, element, details, str, indent, &src_pads, &sink_pads); gst_iterator_free (pad_iter); } if ((pad_iter = gst_element_iterate_src_pads (element))) { debug_dump_element_pads (pad_iter, pad, element, details, str, indent, &src_pads, &sink_pads); gst_iterator_free (pad_iter); } if (GST_IS_BIN (element)) { g_string_append_printf (str, "%s fillcolor=\"#ffffff\";\n", spc); /* recurse */ debug_dump_element (GST_BIN (element), details, str, indent + 1); } else { if (src_pads && !sink_pads) g_string_append_printf (str, "%s fillcolor=\"#ffaaaa\";\n", spc); else if (!src_pads && sink_pads) g_string_append_printf (str, "%s fillcolor=\"#aaaaff\";\n", spc); else if (src_pads && sink_pads) g_string_append_printf (str, "%s fillcolor=\"#aaffaa\";\n", spc); else g_string_append_printf (str, "%s fillcolor=\"#ffffff\";\n", spc); } g_string_append_printf (str, "%s}\n\n", spc); if ((pad_iter = gst_element_iterate_pads (element))) { pads_done = FALSE; while (!pads_done) { switch (gst_iterator_next (pad_iter, &item2)) { case GST_ITERATOR_OK: pad = g_value_get_object (&item2); if (gst_pad_is_linked (pad)) { if (gst_pad_get_direction (pad) == GST_PAD_SRC) { debug_dump_element_pad_link (pad, element, details, str, indent); } else { GstPad *peer_pad = gst_pad_get_peer (pad); if (peer_pad) { if (!GST_IS_GHOST_PAD (peer_pad) && GST_IS_PROXY_PAD (peer_pad)) { debug_dump_element_pad_link (peer_pad, NULL, details, str, indent); } gst_object_unref (peer_pad); } } } g_value_reset (&item2); break; case GST_ITERATOR_RESYNC: gst_iterator_resync (pad_iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: pads_done = TRUE; break; } } g_value_unset (&item2); gst_iterator_free (pad_iter); } g_value_reset (&item); break; case GST_ITERATOR_RESYNC: gst_iterator_resync (element_iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: elements_done = TRUE; break; } } g_value_unset (&item); gst_iterator_free (element_iter); }
/** * gst_ghost_pad_construct: * @gpad: the newly allocated ghost pad * * Finish initialization of a newly allocated ghost pad. * * This function is most useful in language bindings and when subclassing * #GstGhostPad; plugin and application developers normally will not call this * function. Call this function directly after a call to g_object_new * (GST_TYPE_GHOST_PAD, "direction", @dir, ..., NULL). * * Returns: %TRUE if the construction succeeds, %FALSE otherwise. */ gboolean gst_ghost_pad_construct (GstGhostPad * gpad) { GstPadDirection dir, otherdir; GstPadTemplate *templ; GstPad *pad, *internal; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE); g_return_val_if_fail (GST_GHOST_PAD_PRIVATE (gpad)->constructed == FALSE, FALSE); g_object_get (gpad, "direction", &dir, "template", &templ, NULL); g_return_val_if_fail (dir != GST_PAD_UNKNOWN, FALSE); pad = GST_PAD (gpad); /* Set directional padfunctions for ghostpad */ if (dir == GST_PAD_SINK) { gst_pad_set_chain_function (pad, gst_proxy_pad_chain_default); gst_pad_set_chain_list_function (pad, gst_proxy_pad_chain_list_default); } else { gst_pad_set_getrange_function (pad, gst_proxy_pad_getrange_default); } /* INTERNAL PAD, it always exists and is child of the ghostpad */ otherdir = (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC; if (templ) { internal = g_object_new (GST_TYPE_PROXY_PAD, "name", NULL, "direction", otherdir, "template", templ, NULL); /* release ref obtained via g_object_get */ gst_object_unref (templ); } else { internal = g_object_new (GST_TYPE_PROXY_PAD, "name", NULL, "direction", otherdir, NULL); } GST_PAD_UNSET_FLUSHING (internal); /* Set directional padfunctions for internal pad */ if (dir == GST_PAD_SRC) { gst_pad_set_chain_function (internal, gst_proxy_pad_chain_default); gst_pad_set_chain_list_function (internal, gst_proxy_pad_chain_list_default); } else { gst_pad_set_getrange_function (internal, gst_proxy_pad_getrange_default); } GST_OBJECT_LOCK (pad); /* now make the ghostpad a parent of the internal pad */ if (!gst_object_set_parent (GST_OBJECT_CAST (internal), GST_OBJECT_CAST (pad))) goto parent_failed; /* The ghostpad is the parent of the internal pad and is the only object that * can have a refcount on the internal pad. * At this point, the GstGhostPad has a refcount of 1, and the internal pad has * a refcount of 1. * When the refcount of the GstGhostPad drops to 0, the ghostpad will dispose * its refcount on the internal pad in the dispose method by un-parenting it. * This is why we don't take extra refcounts in the assignments below */ GST_PROXY_PAD_INTERNAL (pad) = internal; GST_PROXY_PAD_INTERNAL (internal) = pad; /* special activation functions for the internal pad */ gst_pad_set_activatemode_function (internal, gst_ghost_pad_internal_activate_mode_default); GST_OBJECT_UNLOCK (pad); GST_GHOST_PAD_PRIVATE (gpad)->constructed = TRUE; return TRUE; /* ERRORS */ parent_failed: { GST_WARNING_OBJECT (gpad, "Could not set internal pad %s:%s", GST_DEBUG_PAD_NAME (internal)); g_critical ("Could not set internal pad %s:%s", GST_DEBUG_PAD_NAME (internal)); GST_OBJECT_UNLOCK (pad); gst_object_unref (internal); return FALSE; } }
/* * debug_dump_element: * @bin: the bin that should be analyzed * @out: file to write to * @indent: level of graph indentation * * Helper for _gst_debug_bin_to_dot_file() to recursively dump a pipeline. */ static void debug_dump_element (GstBin * bin, GstDebugGraphDetails details, FILE * out, const gint indent) { GstIterator *element_iter, *pad_iter; gboolean elements_done, pads_done; GstElement *element, *peer_element, *target_element; GstPad *pad, *peer_pad, *target_pad; GstPadDirection dir; GstCaps *caps; GstStructure *structure; gboolean free_caps, free_media; guint src_pads, sink_pads; gchar *media = NULL; gchar *pad_name, *element_name; gchar *peer_pad_name, *peer_element_name; gchar *target_pad_name, *target_element_name; gchar *color_name; gchar *state_name = NULL; gchar *param_name = NULL; gchar *spc = NULL; spc = g_malloc (1 + indent * 2); memset (spc, 32, indent * 2); spc[indent * 2] = '\0'; element_iter = gst_bin_iterate_elements (bin); elements_done = FALSE; while (!elements_done) { switch (gst_iterator_next (element_iter, (gpointer) & element)) { case GST_ITERATOR_OK: element_name = debug_dump_make_object_name (GST_OBJECT (element)); if (details & GST_DEBUG_GRAPH_SHOW_STATES) { state_name = debug_dump_get_element_state (GST_ELEMENT (element)); } if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) { param_name = debug_dump_get_element_params (GST_ELEMENT (element)); } /* elements */ fprintf (out, "%ssubgraph cluster_%s {\n", spc, element_name); fprintf (out, "%s fontname=\"Bitstream Vera Sans\";\n", spc); fprintf (out, "%s fontsize=\"8\";\n", spc); fprintf (out, "%s style=filled;\n", spc); fprintf (out, "%s color=black;\n\n", spc); fprintf (out, "%s label=\"<%s>\\n%s%s%s\";\n", spc, G_OBJECT_TYPE_NAME (element), GST_OBJECT_NAME (element), (state_name ? state_name : ""), (param_name ? param_name : "") ); if (state_name) { g_free (state_name); state_name = NULL; } if (param_name) { g_free (param_name); param_name = NULL; } g_free (element_name); src_pads = sink_pads = 0; if ((pad_iter = gst_element_iterate_pads (element))) { pads_done = FALSE; while (!pads_done) { switch (gst_iterator_next (pad_iter, (gpointer) & pad)) { case GST_ITERATOR_OK: dir = gst_pad_get_direction (pad); pad_name = debug_dump_make_object_name (GST_OBJECT (pad)); element_name = debug_dump_make_object_name (GST_OBJECT (element)); if (GST_IS_GHOST_PAD (pad)) { color_name = (dir == GST_PAD_SRC) ? "#ffdddd" : ((dir == GST_PAD_SINK) ? "#ddddff" : "#ffffff"); } else { color_name = (dir == GST_PAD_SRC) ? "#ffaaaa" : ((dir == GST_PAD_SINK) ? "#aaaaff" : "#cccccc"); } /* pads */ fprintf (out, "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s\"];\n", spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad)); if (dir == GST_PAD_SRC) src_pads++; else if (dir == GST_PAD_SINK) sink_pads++; g_free (pad_name); g_free (element_name); gst_object_unref (pad); break; case GST_ITERATOR_RESYNC: gst_iterator_resync (pad_iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: pads_done = TRUE; break; } } gst_iterator_free (pad_iter); } if (GST_IS_BIN (element)) { fprintf (out, "%s fillcolor=\"#ffffff\";\n", spc); /* recurse */ debug_dump_element (GST_BIN (element), details, out, indent + 1); } else { if (src_pads && !sink_pads) fprintf (out, "%s fillcolor=\"#ffaaaa\";\n", spc); else if (!src_pads && sink_pads) fprintf (out, "%s fillcolor=\"#aaaaff\";\n", spc); else if (src_pads && sink_pads) fprintf (out, "%s fillcolor=\"#aaffaa\";\n", spc); else fprintf (out, "%s fillcolor=\"#ffffff\";\n", spc); } fprintf (out, "%s}\n\n", spc); if ((pad_iter = gst_element_iterate_pads (element))) { pads_done = FALSE; while (!pads_done) { switch (gst_iterator_next (pad_iter, (gpointer) & pad)) { case GST_ITERATOR_OK: if (gst_pad_is_linked (pad) && gst_pad_get_direction (pad) == GST_PAD_SRC) { if ((peer_pad = gst_pad_get_peer (pad))) { free_media = FALSE; if ((details & GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE) || (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) ) { if ((caps = gst_pad_get_negotiated_caps (pad))) { free_caps = TRUE; } else { free_caps = FALSE; if (!(caps = (GstCaps *) gst_pad_get_pad_template_caps (pad))) { /* this should not happen */ media = "?"; } } if (caps) { if (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) { gchar *tmp = g_strdelimit (gst_caps_to_string (caps), ",", '\n'); media = g_strescape (tmp, NULL); free_media = TRUE; g_free (tmp); } else { if (GST_CAPS_IS_SIMPLE (caps)) { structure = gst_caps_get_structure (caps, 0); media = (gchar *) gst_structure_get_name (structure); } else media = "*"; } if (free_caps) { gst_caps_unref (caps); } } } pad_name = debug_dump_make_object_name (GST_OBJECT (pad)); element_name = debug_dump_make_object_name (GST_OBJECT (element)); peer_pad_name = debug_dump_make_object_name (GST_OBJECT (peer_pad)); if ((peer_element = gst_pad_get_parent_element (peer_pad))) { peer_element_name = debug_dump_make_object_name (GST_OBJECT (peer_element)); } else { peer_element_name = ""; } /* pad link */ if (media) { fprintf (out, "%s%s_%s -> %s_%s [label=\"%s\"]\n", spc, element_name, pad_name, peer_element_name, peer_pad_name, media); if (free_media) { g_free (media); } } else { fprintf (out, "%s%s_%s -> %s_%s\n", spc, element_name, pad_name, peer_element_name, peer_pad_name); } if (GST_IS_GHOST_PAD (pad)) { if ((target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) { target_pad_name = debug_dump_make_object_name (GST_OBJECT (target_pad)); if ((target_element = gst_pad_get_parent_element (target_pad))) { target_element_name = debug_dump_make_object_name (GST_OBJECT (target_element)); } else { target_element_name = ""; } /* src ghostpad relationship */ fprintf (out, "%s%s_%s -> %s_%s [style=dashed]\n", spc, target_element_name, target_pad_name, element_name, pad_name); g_free (target_pad_name); if (target_element) { g_free (target_element_name); gst_object_unref (target_element); } gst_object_unref (target_pad); } } if (GST_IS_GHOST_PAD (peer_pad)) { if ((target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer_pad)))) { target_pad_name = debug_dump_make_object_name (GST_OBJECT (target_pad)); if ((target_element = gst_pad_get_parent_element (target_pad))) { target_element_name = debug_dump_make_object_name (GST_OBJECT (target_element)); } else { target_element_name = ""; } /* sink ghostpad relationship */ fprintf (out, "%s%s_%s -> %s_%s [style=dashed]\n", spc, peer_element_name, peer_pad_name, target_element_name, target_pad_name); g_free (target_pad_name); if (target_element) { g_free (target_element_name); gst_object_unref (target_element); } gst_object_unref (target_pad); } } g_free (pad_name); g_free (element_name); g_free (peer_pad_name); if (peer_element) { g_free (peer_element_name); gst_object_unref (peer_element); } gst_object_unref (peer_pad); } } gst_object_unref (pad); break; case GST_ITERATOR_RESYNC: gst_iterator_resync (pad_iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: pads_done = TRUE; break; } } gst_iterator_free (pad_iter); } gst_object_unref (element); break; case GST_ITERATOR_RESYNC: gst_iterator_resync (element_iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: elements_done = TRUE; break; } } gst_iterator_free (element_iter); g_free (spc); }
/** * gst_ghost_pad_construct: * @gpad: the newly allocated ghost pad * * Finish initialization of a newly allocated ghost pad. * * This function is most useful in language bindings and when subclassing * #GstGhostPad; plugin and application developers normally will not call this * function. Call this function directly after a call to g_object_new * (GST_TYPE_GHOST_PAD, "direction", @dir, ..., NULL). * * Returns: %TRUE if the construction succeeds, %FALSE otherwise. * * Since: 0.10.22 */ gboolean gst_ghost_pad_construct (GstGhostPad * gpad) { GstPadDirection dir, otherdir; GstPadTemplate *templ; GstPad *pad, *internal; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE); g_return_val_if_fail (GST_GHOST_PAD_PRIVATE (gpad)->constructed == FALSE, FALSE); g_object_get (gpad, "direction", &dir, "template", &templ, NULL); g_return_val_if_fail (dir != GST_PAD_UNKNOWN, FALSE); pad = GST_PAD (gpad); /* Set directional padfunctions for ghostpad */ if (dir == GST_PAD_SINK) { gst_pad_set_bufferalloc_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc)); gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain)); } else { gst_pad_set_getrange_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange)); gst_pad_set_checkgetrange_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange)); } /* link/unlink functions */ gst_pad_set_link_function (pad, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link)); gst_pad_set_unlink_function (pad, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_unlink)); /* INTERNAL PAD, it always exists and is child of the ghostpad */ otherdir = (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC; if (templ) { internal = g_object_new (GST_TYPE_PROXY_PAD, "name", NULL, "direction", otherdir, "template", templ, NULL); /* release ref obtained via g_object_get */ gst_object_unref (templ); } else { internal = g_object_new (GST_TYPE_PROXY_PAD, "name", NULL, "direction", otherdir, NULL); } GST_PAD_UNSET_FLUSHING (internal); /* Set directional padfunctions for internal pad */ if (dir == GST_PAD_SRC) { gst_pad_set_bufferalloc_function (internal, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc)); gst_pad_set_chain_function (internal, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain)); } else { gst_pad_set_getrange_function (internal, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange)); gst_pad_set_checkgetrange_function (internal, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange)); } GST_PROXY_LOCK (pad); /* now make the ghostpad a parent of the internal pad */ if (!gst_object_set_parent (GST_OBJECT_CAST (internal), GST_OBJECT_CAST (pad))) goto parent_failed; /* The ghostpad is the parent of the internal pad and is the only object that * can have a refcount on the internal pad. * At this point, the GstGhostPad has a refcount of 1, and the internal pad has * a refcount of 1. * When the refcount of the GstGhostPad drops to 0, the ghostpad will dispose * it's refcount on the internal pad in the dispose method by un-parenting it. * This is why we don't take extra refcounts in the assignments below */ GST_PROXY_PAD_INTERNAL (pad) = internal; GST_PROXY_PAD_INTERNAL (internal) = pad; /* could be more general here, iterating over all writable properties... * taking the short road for now tho */ GST_GHOST_PAD_PRIVATE (pad)->notify_id = g_signal_connect (internal, "notify::caps", G_CALLBACK (on_int_notify), pad); /* call function to init values of the pad caps */ on_int_notify (internal, NULL, GST_GHOST_PAD_CAST (pad)); /* special activation functions for the internal pad */ gst_pad_set_activatepull_function (internal, GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull)); gst_pad_set_activatepush_function (internal, GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push)); GST_PROXY_UNLOCK (pad); GST_GHOST_PAD_PRIVATE (gpad)->constructed = TRUE; return TRUE; /* ERRORS */ parent_failed: { GST_WARNING_OBJECT (gpad, "Could not set internal pad %s:%s", GST_DEBUG_PAD_NAME (internal)); g_critical ("Could not set internal pad %s:%s", GST_DEBUG_PAD_NAME (internal)); GST_PROXY_UNLOCK (pad); gst_object_unref (internal); return FALSE; } }