/* GStreamer
* Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
*
- * gstdebugutils.c: debugging and analysis utillities
+ * gstdebugutils.c: debugging and analysis utilities
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
/* TODO:
* edge [ constraint=false ];
* this creates strange graphs ("minlen=0" is better)
- * try puting src/sink ghostpads for each bin into invisible clusters
+ * try putting src/sink ghostpads for each bin into invisible clusters
*
* for more compact nodes, try
* - changing node-shape from box into record
#define PARAM_MAX_LENGTH 80
-const gchar spaces[] = {
+static const gchar spaces[] = {
" " /* 32 */
" " /* 64 */
" " /* 96 */
}
static gchar *
-debug_dump_get_element_params (GstElement * element,
- GstDebugGraphDetails details)
+debug_dump_get_object_params (GObject * object,
+ GstDebugGraphDetails details, const char *const *ignored_propnames)
{
gchar *param_name = NULL;
GParamSpec **properties, *property;
/* get paramspecs and show non-default properties */
properties =
- g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
- (element)), &number_of_properties);
+ g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
+ &number_of_properties);
if (properties) {
for (i = 0; i < number_of_properties; i++) {
+ gint j;
+ gboolean ignore = FALSE;
property = properties[i];
/* skip some properties */
if (!strcmp (property->name, "name"))
continue;
+ if (ignored_propnames)
+ for (j = 0; ignored_propnames[j]; j++)
+ if (!g_strcmp0 (ignored_propnames[j], property->name))
+ ignore = TRUE;
+
+ if (ignore)
+ continue;
+
g_value_init (&value, property->value_type);
- g_object_get_property (G_OBJECT (element), property->name, &value);
+ g_object_get_property (G_OBJECT (object), property->name, &value);
if (!(g_param_value_defaults (property, &value))) {
- tmp = g_strdup_value_contents (&value);
+ /* we need to serialise enums and flags ourselves to make sure the
+ * enum/flag nick is used and not the enum/flag name, which would be the
+ * C header enum/flag for public enums/flags, but for element-specific
+ * enums/flags we abuse the name field for the property description,
+ * and we don't want to print that in the dot file. The nick will
+ * always work, and it's also shorter. */
+ if (G_VALUE_HOLDS_ENUM (&value)) {
+ GEnumClass *e_class = g_type_class_ref (G_VALUE_TYPE (&value));
+ gint idx, e_val;
+
+ tmp = NULL;
+ e_val = g_value_get_enum (&value);
+ for (idx = 0; idx < e_class->n_values; ++idx) {
+ if (e_class->values[idx].value == e_val) {
+ tmp = g_strdup (e_class->values[idx].value_nick);
+ break;
+ }
+ }
+ if (tmp == NULL) {
+ g_value_unset (&value);
+ continue;
+ }
+ } else if (G_VALUE_HOLDS_FLAGS (&value)) {
+ GFlagsClass *f_class = g_type_class_ref (G_VALUE_TYPE (&value));
+ GFlagsValue *vals = f_class->values;
+ GString *s = NULL;
+ guint idx, flags_left;
+
+ s = g_string_new (NULL);
+
+ /* we assume the values are sorted from lowest to highest value */
+ flags_left = g_value_get_flags (&value);
+ idx = f_class->n_values;
+ while (idx > 0) {
+ --idx;
+ if (vals[idx].value != 0
+ && (flags_left & vals[idx].value) == vals[idx].value) {
+ if (s->len > 0)
+ g_string_prepend_c (s, '+');
+ g_string_prepend (s, vals[idx].value_nick);
+ flags_left -= vals[idx].value;
+ if (flags_left == 0)
+ break;
+ }
+ }
+
+ if (s->len == 0)
+ g_string_assign (s, "(none)");
+
+ tmp = g_string_free (s, FALSE);
+ } else {
+ tmp = g_strdup_value_contents (&value);
+ }
value_str = g_strescape (tmp, NULL);
g_free (tmp);
{
GstPadTemplate *pad_templ;
GstPadPresence presence;
- gchar *pad_name;
+ gchar *pad_name, *param_name = NULL;
const gchar *style_name;
+ static const char *const ignore_propnames[] =
+ { "parent", "direction", "template",
+ "caps", NULL
+ };
const gchar *spc = MAKE_INDENT (indent);
pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
style_name = "filled,dashed";
}
}
+
+ param_name =
+ debug_dump_get_object_params (G_OBJECT (pad), details, ignore_propnames);
if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
- gchar pad_flags[4];
+ gchar pad_flags[5];
const gchar *activation_mode = "-><";
const gchar *task_mode = "";
GstTask *task;
GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_FLUSHING) ? 'F' : 'f';
pad_flags[2] =
GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_BLOCKING) ? 'B' : 'b';
- pad_flags[3] = '\0';
+ pad_flags[3] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_EOS) ? 'E' : '\0';
+ pad_flags[4] = '\0';
g_string_append_printf (str,
- "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s\\n[%c][%s]%s\", height=\"0.2\", style=\"%s\"];\n",
+ "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s%s\\n[%c][%s]%s\", height=\"0.2\", style=\"%s\"];\n",
spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad),
+ (param_name ? param_name : ""),
activation_mode[pad->mode], pad_flags, task_mode, style_name);
} else {
g_string_append_printf (str,
- "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s\", height=\"0.2\", style=\"%s\"];\n",
+ "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s%s\", height=\"0.2\", style=\"%s\"];\n",
spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad),
- style_name);
+ (param_name ? param_name : ""), style_name);
}
+ g_free (param_name);
g_free (pad_name);
}
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);
+ param_name = debug_dump_get_object_params (G_OBJECT (element),
+ details, NULL);
}
/* elements */
g_string_append_printf (str, "%ssubgraph cluster_%s {\n", spc,
state_name = debug_dump_get_element_state (GST_ELEMENT (bin));
}
if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
- param_name = debug_dump_get_element_params (GST_ELEMENT (bin), details);
+ param_name = debug_dump_get_object_params (G_OBJECT (bin), details, NULL);
}
/* write header */
" pos=\"0,0!\",\n"
" margin=\"0.05,0.05\",\n"
" style=\"filled\",\n"
- " label=\"Legend\\lElement-States: [~] void-pending, [0] null, [-] ready, [=] paused, [>] playing\\lPad-Activation: [-] none, [>] push, [<] pull\\lPad-Flags: [b]locked, [f]lushing, [b]locking; upper-case is set\\lPad-Task: [T] has started task, [t] has paused task\\l\",\n"
+ " label=\"Legend\\lElement-States: [~] void-pending, [0] null, [-] ready, [=] paused, [>] playing\\lPad-Activation: [-] none, [>] push, [<] pull\\lPad-Flags: [b]locked, [f]lushing, [b]locking, [E]OS; upper-case is set\\lPad-Task: [T] has started task, [t] has paused task\\l\",\n"
" ];"
"\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
(state_name ? state_name : ""), (param_name ? param_name : "")
g_string_append_printf (str, "}\n");
}
-/*
+/**
* gst_debug_bin_to_dot_data:
* @bin: the top-level pipeline that should be analyzed
+ * @details: type of #GstDebugGraphDetails to use
*
* To aid debugging applications one can use this method to obtain the whole
* network of gstreamer elements that form the pipeline into an dot file.
return g_string_free (str, FALSE);
}
-/*
+/**
* gst_debug_bin_to_dot_file:
* @bin: the top-level pipeline that should be analyzed
- * @file_name: output base filename (e.g. "myplayer")
+ * @details: type of #GstDebugGraphDetails to use
+ * @file_name: (type filename): output base filename (e.g. "myplayer")
*
* To aid debugging applications one can use this method to write out the whole
* network of gstreamer elements that form the pipeline into an dot file.
g_free (full_file_name);
}
-/*
+/**
* gst_debug_bin_to_dot_file_with_ts:
* @bin: the top-level pipeline that should be analyzed
- * @file_name: output base filename (e.g. "myplayer")
+ * @details: type of #GstDebugGraphDetails to use
+ * @file_name: (type filename): output base filename (e.g. "myplayer")
*
* This works like gst_debug_bin_to_dot_file(), but adds the current timestamp
* to the filename, so that it can be used to take multiple snapshots.
}
/* add timestamp */
- elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
- gst_util_get_timestamp ());
+ elapsed = GST_CLOCK_DIFF (_priv_gst_start_time, gst_util_get_timestamp ());
/* we don't use GST_TIME_FORMAT as such filenames would fail on some
* filesystems like fat */