mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-07 06:52:41 +00:00
Allow dumping pipelines as dot graphs. Fixes #456573.
Original commit message from CVS: * docs/gst/gstreamer-sections.txt: * gst/Makefile.am: * gst/gst.c: * gst/gst.h: * gst/gstdebugutils.c: * gst/gstdebugutils.h: * gst/gstinfo.c: * gst/gstinfo.h: * tools/gst-launch.c: Allow dumping pipelines as dot graphs. Fixes #456573.
This commit is contained in:
parent
4945ceea39
commit
58a155347b
10 changed files with 675 additions and 4 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
|||
2007-10-17 Stefan Kost <ensonic@users.sf.net>
|
||||
|
||||
* docs/gst/gstreamer-sections.txt:
|
||||
* gst/Makefile.am:
|
||||
* gst/gst.c:
|
||||
* gst/gst.h:
|
||||
* gst/gstdebugutils.c:
|
||||
* gst/gstdebugutils.h:
|
||||
* gst/gstinfo.c:
|
||||
* gst/gstinfo.h:
|
||||
* tools/gst-launch.c:
|
||||
Allow dumping pipelines as dot graphs. Fixes #456573.
|
||||
|
||||
2007-10-16 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* gst/gststructure.c:
|
||||
|
|
|
@ -855,6 +855,7 @@ GstDebugLevel
|
|||
GST_LEVEL_DEFAULT
|
||||
GstDebugColorFlags
|
||||
GstDebugCategory
|
||||
GstDebugGraphDetails
|
||||
GST_STR_NULL
|
||||
GST_DEBUG_PAD_NAME
|
||||
GST_FUNCTION
|
||||
|
@ -911,12 +912,15 @@ GST_DEBUG
|
|||
GST_LOG
|
||||
GST_DEBUG_FUNCPTR
|
||||
GST_DEBUG_FUNCPTR_NAME
|
||||
GST_DEBUG_BIN_TO_DOT_FILE
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS
|
||||
gst_debug_print_stack_trace
|
||||
GST_TIME_FORMAT
|
||||
GST_TIME_ARGS
|
||||
<SUBSECTION Standard>
|
||||
GST_TYPE_DEBUG_COLOR_FLAGS
|
||||
GST_TYPE_DEBUG_LEVEL
|
||||
GST_TYPE_DEBUG_GRAPH_DETAILS
|
||||
<SUBSECTION Private>
|
||||
GST_DEBUG_FG_MASK
|
||||
GST_DEBUG_BG_MASK
|
||||
|
@ -925,6 +929,7 @@ GstDebugFuncPtr
|
|||
GstDebugMessage
|
||||
gst_debug_color_flags_get_type
|
||||
gst_debug_level_get_type
|
||||
gst_debug_graph_details_get_type
|
||||
IMPORT_SYMBOL
|
||||
M_PI
|
||||
</SECTION>
|
||||
|
|
|
@ -82,6 +82,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
|
|||
gstcaps.c \
|
||||
gstchildproxy.c \
|
||||
gstclock.c \
|
||||
gstdebugutils.c \
|
||||
gstelement.c \
|
||||
gstelementfactory.c \
|
||||
gsterror.c \
|
||||
|
@ -162,6 +163,7 @@ gst_headers = \
|
|||
gstchildproxy.h \
|
||||
gstclock.h \
|
||||
gstcompat.h \
|
||||
gstdebugutils.h \
|
||||
gstelement.h \
|
||||
gstelementfactory.h \
|
||||
gsterror.h \
|
||||
|
|
|
@ -134,6 +134,7 @@ static GList *plugin_paths = NULL; /* for delayed processing in post_init *
|
|||
#endif
|
||||
|
||||
extern gint _gst_trace_on;
|
||||
extern gboolean _gst_debug_dump_dot_files_on;
|
||||
|
||||
/* defaults */
|
||||
#ifdef HAVE_FORK
|
||||
|
@ -585,6 +586,9 @@ init_pre (GOptionContext * context, GOptionGroup * group, gpointer data,
|
|||
if (debug_list) {
|
||||
parse_debug_list (debug_list);
|
||||
}
|
||||
|
||||
if (g_getenv ("GST_DEBUG_DUMP_DOT_FILES") != NULL)
|
||||
_gst_debug_dump_dot_files_on = TRUE;
|
||||
}
|
||||
#endif
|
||||
/* This is the earliest we can make stuff show up in the logs.
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <gst/gstcaps.h>
|
||||
#include <gst/gstchildproxy.h>
|
||||
#include <gst/gstclock.h>
|
||||
#include <gst/gstdebugutils.h>
|
||||
#include <gst/gstelement.h>
|
||||
#include <gst/gsterror.h>
|
||||
#include <gst/gstevent.h>
|
||||
|
|
517
gst/gstdebugutils.c
Normal file
517
gst/gstdebugutils.c
Normal file
|
@ -0,0 +1,517 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
|
||||
*
|
||||
* gstdebugutils.c: debugging and analysis utillities
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
|
||||
#include "gst_private.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gstinfo.h"
|
||||
#include "gstdebugutils.h"
|
||||
#include "gstbin.h"
|
||||
#include "gstobject.h"
|
||||
#include "gstghostpad.h"
|
||||
#include "gstpad.h"
|
||||
#include "gstutils.h"
|
||||
|
||||
/*** PIPELINE GRAPHS **********************************************************/
|
||||
|
||||
gboolean _gst_debug_dump_dot_files_on = FALSE;
|
||||
extern GstClockTime _gst_info_start_time;
|
||||
|
||||
static gchar *
|
||||
debug_dump_make_object_name (GstObject * element)
|
||||
{
|
||||
return g_strcanon (g_strdup_printf ("%s_%p", GST_OBJECT_NAME (element),
|
||||
element), G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_", '_');
|
||||
}
|
||||
|
||||
static gchar *
|
||||
debug_dump_get_element_state (GstElement * element)
|
||||
{
|
||||
gchar *state_name = NULL;
|
||||
const gchar *state_icons = "~0-=>";
|
||||
GstState state = 0, pending = 0;
|
||||
|
||||
gst_element_get_state (element, &state, &pending, 0);
|
||||
if (pending == GST_STATE_VOID_PENDING) {
|
||||
state_name = g_strdup_printf ("\\n[%c]", state_icons[state]);
|
||||
} else {
|
||||
state_name = g_strdup_printf ("\\n[%c]->[%c]", state_icons[state],
|
||||
state_icons[pending]);
|
||||
}
|
||||
return state_name;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
debug_dump_get_element_params (GstElement * element)
|
||||
{
|
||||
gchar *param_name = NULL;
|
||||
GParamSpec **properties, *property;
|
||||
GValue value = { 0, };
|
||||
guint i, number_of_properties;
|
||||
gchar *tmp, *value_str;
|
||||
|
||||
/* get paramspecs and show non-default properties */
|
||||
properties =
|
||||
g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
|
||||
(element)), &number_of_properties);
|
||||
if (properties) {
|
||||
for (i = 0; i < number_of_properties; i++) {
|
||||
property = properties[i];
|
||||
|
||||
/* ski some properties */
|
||||
if (!(property->flags & G_PARAM_READABLE))
|
||||
continue;
|
||||
if (!strcmp (property->name, "name"))
|
||||
continue;
|
||||
|
||||
g_value_init (&value, property->value_type);
|
||||
g_object_get_property (G_OBJECT (element), property->name, &value);
|
||||
if (!(g_param_value_defaults (property, &value))) {
|
||||
tmp = g_strdup_value_contents (&value);
|
||||
value_str = g_strescape (tmp, NULL);
|
||||
g_free (tmp);
|
||||
if (param_name) {
|
||||
tmp = param_name;
|
||||
param_name = g_strdup_printf ("%s\\n%s=%s",
|
||||
tmp, property->name, value_str);
|
||||
g_free (tmp);
|
||||
} else {
|
||||
param_name = g_strdup_printf ("\\n%s=%s", property->name, value_str);
|
||||
}
|
||||
g_free (value_str);
|
||||
}
|
||||
g_value_unset (&value);
|
||||
}
|
||||
g_free (properties);
|
||||
}
|
||||
return param_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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[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 {
|
||||
if ((caps =
|
||||
(GstCaps *)
|
||||
gst_pad_get_pad_template_caps (pad))) {
|
||||
free_caps = FALSE;
|
||||
} else {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* _gst_debug_bin_to_dot_file:
|
||||
* @bin: the top-level pipeline that should be analyzed
|
||||
* @file_name: output filename (e.g. "/tmp/metadata.dot")
|
||||
*
|
||||
* 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.
|
||||
* This file can be processed with graphviz to get an image.
|
||||
* <informalexample><programlisting>
|
||||
* dot -Tpng -oimage.png graph_lowlevel.dot
|
||||
* </programlisting></informalexample>
|
||||
*/
|
||||
void
|
||||
_gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
|
||||
const gchar * file_name)
|
||||
{
|
||||
FILE *out;
|
||||
|
||||
g_return_if_fail (GST_IS_BIN (bin));
|
||||
g_return_if_fail (file_name != NULL);
|
||||
|
||||
if (!_gst_debug_dump_dot_files_on)
|
||||
return;
|
||||
|
||||
if ((out = fopen (file_name, "wb"))) {
|
||||
gchar *state_name = NULL;
|
||||
gchar *param_name = NULL;
|
||||
|
||||
if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
|
||||
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));
|
||||
}
|
||||
|
||||
/* write header */
|
||||
fprintf (out,
|
||||
"digraph pipeline {\n"
|
||||
" rankdir=LR;\n"
|
||||
" fontname=\"Bitstream Vera Sans\";\n"
|
||||
" fontsize=\"8\";\n"
|
||||
" labelloc=t;\n"
|
||||
" nodesep=.15;\n"
|
||||
" label=\"<%s>\\n%s%s%s\";\n"
|
||||
" node [style=filled, shape=box, fontsize=\"7\", fontname=\"Bitstream Vera Sans\"];\n"
|
||||
" edge [labelfontsize=\"7\", fontsize=\"7\", labelfontname=\"Bitstream Vera Sans\", fontname=\"Bitstream Vera Sans\"];\n"
|
||||
"\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
|
||||
(state_name ? state_name : ""), (param_name ? param_name : "")
|
||||
);
|
||||
if (state_name)
|
||||
g_free (state_name);
|
||||
if (param_name)
|
||||
g_free (param_name);
|
||||
|
||||
debug_dump_element (bin, details, out, 1);
|
||||
|
||||
/* write footer */
|
||||
fprintf (out, "}\n");
|
||||
fclose (out);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _gst_debug_bin_to_dot_file_with_ts:
|
||||
* @bin: the top-level pipeline that should be analyzed
|
||||
* @file_tmpl: output filename template
|
||||
* (e.g. "/tmp/metadata.%" GST_TIME_FORMAT ".dot")
|
||||
*
|
||||
* This works like _gst_debug_bin_to_dot_file(), but fills the filename template
|
||||
* with the timestamp, so that it can be used to take multiple snapshots.
|
||||
*/
|
||||
void
|
||||
_gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
|
||||
const gchar * file_tmpl)
|
||||
{
|
||||
gchar *file_name = NULL;
|
||||
const gchar *pos;
|
||||
GTimeVal now;
|
||||
GstClockTime elapsed;
|
||||
guint fmt_ct = 0;
|
||||
|
||||
g_return_if_fail (GST_IS_BIN (bin));
|
||||
g_return_if_fail (file_tmpl != NULL);
|
||||
|
||||
/* check file-name template */
|
||||
pos = strchr (file_tmpl, '%');
|
||||
if (pos) {
|
||||
do {
|
||||
pos++;
|
||||
if (*pos != '\0') {
|
||||
if (*pos != '%')
|
||||
fmt_ct++;
|
||||
pos++;
|
||||
}
|
||||
pos = strchr (pos, '%');
|
||||
} while (pos);
|
||||
}
|
||||
if (fmt_ct == 0) {
|
||||
GST_WARNING ("file template has no valid placeholder");
|
||||
return;
|
||||
} else if (fmt_ct != 4) {
|
||||
GST_WARNING ("file template must have 4 placeholders, but has %d", fmt_ct);
|
||||
return;
|
||||
}
|
||||
|
||||
/* add timestamp */
|
||||
g_get_current_time (&now);
|
||||
elapsed = GST_TIMEVAL_TO_TIME (now) - _gst_info_start_time;
|
||||
file_name = g_strdup_printf (file_tmpl, GST_TIME_ARGS (elapsed));
|
||||
|
||||
_gst_debug_bin_to_dot_file (bin, details, file_name);
|
||||
g_free (file_name);
|
||||
}
|
||||
|
||||
#endif /* GST_DISABLE_GST_DEBUG */
|
102
gst/gstdebugutils.h
Normal file
102
gst/gstdebugutils.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
|
||||
*
|
||||
* gstdebugutils.h: debugging and analysis utillities
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GSTDEBUGUTILS_H__
|
||||
#define __GSTDEBUGUTILS_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/gstconfig.h>
|
||||
#include <gst/gstbin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstDebugGraphDetails:
|
||||
* @GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE: show caps-name on edges
|
||||
* @GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS: show caps-details on edges
|
||||
* @GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS: show modified parameters on elements
|
||||
* @GST_DEBUG_GRAPH_SHOW_STATES: show element states
|
||||
* @GST_DEBUG_GRAPH_SHOW_ALL: show all details
|
||||
*
|
||||
* Available details for pipeline graphs produced by GST_DEBUG_BIN_TO_DOT_FILE()
|
||||
* and GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS().
|
||||
*/
|
||||
typedef enum {
|
||||
GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE = (1<<0),
|
||||
GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS = (1<<1),
|
||||
GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS = (1<<2),
|
||||
GST_DEBUG_GRAPH_SHOW_STATES = (1<<3),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL = ((1<<4)-1)
|
||||
} GstDebugGraphDetails;
|
||||
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
|
||||
/********** pipeline graphs **********/
|
||||
|
||||
void _gst_debug_bin_to_dot_file (GstBin *bin, GstDebugGraphDetails details, const gchar *file_name);
|
||||
void _gst_debug_bin_to_dot_file_with_ts (GstBin *bin, GstDebugGraphDetails details, const gchar *file_tmpl);
|
||||
|
||||
/**
|
||||
* GST_DEBUG_BIN_TO_DOT_FILE:
|
||||
* @bin: the top-level pipeline that should be analyzed
|
||||
* @details: graph-details to show
|
||||
* @file_name: output filename (e.g. "/tmp/metadata.dot")
|
||||
*
|
||||
* 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.
|
||||
* This file can be processed with graphviz to get an image.
|
||||
* <informalexample><programlisting>
|
||||
* dot -Tpng -oimage.png graph_lowlevel.dot
|
||||
* </programlisting></informalexample>
|
||||
*
|
||||
* The macro is only active if gstreamer is configured with
|
||||
* "--gst-enable-gst-debug" and the environment variable
|
||||
* GST_DEBUG_DUMP_DOT_FILES is set (e.g. to 1).
|
||||
*/
|
||||
#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name) _gst_debug_bin_to_dot_file (bin, details, file_name)
|
||||
|
||||
/**
|
||||
* GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS:
|
||||
* @bin: the top-level pipeline that should be analyzed
|
||||
* @details: graph-details to show
|
||||
* @file_tmpl: output filename template
|
||||
* (e.g. "/tmp/metadata.%" GST_TIME_FORMAT ".dot")
|
||||
*
|
||||
* This works like _gst_debug_bin_to_dot_file(), but fills the filename template
|
||||
* with the timestamp, so that it can be used to take multiple snapshots.
|
||||
*/
|
||||
#define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_tmpl) _gst_debug_bin_to_dot_file_with_ts (bin, details, file_tmpl)
|
||||
|
||||
|
||||
#else /* GST_DISABLE_GST_DEBUG */
|
||||
|
||||
|
||||
#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name)
|
||||
#define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_tmpl)
|
||||
|
||||
#endif /* GST_DISABLE_GST_DEBUG */
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTDEBUGUTILS_H__ */
|
||||
|
|
@ -115,8 +115,12 @@
|
|||
|
||||
/* underscore is to prevent conflict with GST_CAT_DEBUG define */
|
||||
GST_DEBUG_CATEGORY_STATIC (_GST_CAT_DEBUG);
|
||||
/* time of initialization, so we get useful debugging output times */
|
||||
static GstClockTime start_time;
|
||||
|
||||
/* time of initialization, so we get useful debugging output times
|
||||
* FIXME: we use this in gstdebugutils.c, what about a function + macro to
|
||||
* get the running time: GST_DEBUG_RUNNING_TIME
|
||||
*/
|
||||
GstClockTime _gst_info_start_time;
|
||||
|
||||
#if 0
|
||||
#if defined __sgi__
|
||||
|
@ -280,7 +284,7 @@ _gst_debug_init (void)
|
|||
|
||||
/* get time we started for debugging messages */
|
||||
g_get_current_time (¤t);
|
||||
start_time = GST_TIMEVAL_TO_TIME (current);
|
||||
_gst_info_start_time = GST_TIMEVAL_TO_TIME (current);
|
||||
|
||||
#ifdef HAVE_PRINTF_EXTENSION
|
||||
register_printf_function (GST_PTR_FORMAT[0], _gst_info_printf_extension_ptr,
|
||||
|
@ -666,7 +670,7 @@ gst_debug_log_default (GstDebugCategory * category, GstDebugLevel level,
|
|||
}
|
||||
|
||||
g_get_current_time (&now);
|
||||
elapsed = GST_TIMEVAL_TO_TIME (now) - start_time;
|
||||
elapsed = GST_TIMEVAL_TO_TIME (now) - _gst_info_start_time;
|
||||
|
||||
/*
|
||||
g_printerr ("%s (%p - %" GST_TIME_FORMAT ") %s%20s%s(%s%5d%s) %s%s(%d):%s:%s%s %s\n",
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <glib-object.h>
|
||||
#include <gst/gstconfig.h>
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define IMPORT_SYMBOL
|
||||
#else /* _MSC_VER */
|
||||
|
@ -928,8 +929,10 @@ G_CONST_RETURN gchar *
|
|||
#define GST_DEBUG_FUNCPTR_NAME(ptr) \
|
||||
_gst_debug_nameof_funcptr((GstDebugFuncPtr)ptr)
|
||||
|
||||
|
||||
#else /* GST_DISABLE_GST_DEBUG */
|
||||
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
# pragma GCC poison gst_debug_log
|
||||
# pragma GCC poison gst_debug_log_valist
|
||||
|
@ -1149,6 +1152,7 @@ GST_LOG (const char *format, ...)
|
|||
|
||||
#endif /* GST_DISABLE_GST_DEBUG */
|
||||
|
||||
|
||||
void gst_debug_print_stack_trace (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -450,6 +450,11 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
|
|||
gchar *debug;
|
||||
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
|
||||
|
||||
/* dump graph on warning */
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL,
|
||||
"/tmp/gst-launch.warning.%" GST_TIME_FORMAT ".dot");
|
||||
|
||||
gst_message_parse_warning (message, &gerror, &debug);
|
||||
g_print (_("WARNING: from element %s: %s\n"), name, gerror->message);
|
||||
if (debug) {
|
||||
|
@ -464,6 +469,11 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
|
|||
GError *gerror;
|
||||
gchar *debug;
|
||||
|
||||
/* dump graph on error */
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL,
|
||||
"/tmp/gst-launch.error.%" GST_TIME_FORMAT ".dot");
|
||||
|
||||
gst_message_parse_error (message, &gerror, &debug);
|
||||
gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
|
||||
g_error_free (gerror);
|
||||
|
@ -477,10 +487,19 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
|
|||
|
||||
gst_message_parse_state_changed (message, &old, &new, &pending);
|
||||
|
||||
/* debug each state change
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline),GST_DEBUG_GRAPH_SHOW_ALL,"/tmp/gst-launch.%" GST_TIME_FORMAT ".dot");
|
||||
*/
|
||||
|
||||
/* we only care about pipeline state change messages */
|
||||
if (GST_MESSAGE_SRC (message) != GST_OBJECT_CAST (pipeline))
|
||||
break;
|
||||
|
||||
/* debug only overall state changes
|
||||
FIXME: add statename to name template: gst_element_state_get_name(new);
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline),GST_DEBUG_GRAPH_SHOW_ALL,"/tmp/gst-launch.%" GST_TIME_FORMAT ".dot");
|
||||
*/
|
||||
|
||||
/* ignore when we are buffering since then we mess with the states
|
||||
* ourselves. */
|
||||
if (buffering) {
|
||||
|
|
Loading…
Reference in a new issue