gst: Remove dead assignments
[platform/upstream/gstreamer.git] / gst / gstdebugutils.c
1 /* GStreamer
2  * Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstdebugutils.c: debugging and analysis utillities
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /* TODO:
22  * edge [ constraint=false ];
23  *   this creates strange graphs ("minlen=0" is better)
24  * try puting src/sink ghostpads for each bin into invisible clusters
25  *
26  * for more compact nodes, try
27  * - changing node-shape from box into record
28  * - use labels like : element [ label="{element | <src> src | <sink> sink}"]
29  * - point to record-connectors : element1:src -> element2:sink
30  * - we use head/tail labels for pad-caps right now
31  *   - this does not work well, as dot seems to not llok at their sizen when
32  *     doing the layout
33  *   - we could add the caps to the pad itself, then we should use one line per
34  *     caps (simple caps = one line)
35  */
36
37 #include "gst_private.h"
38 #include "gstdebugutils.h"
39
40 #ifndef GST_DISABLE_GST_DEBUG
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46
47 #include "gstinfo.h"
48 #include "gstbin.h"
49 #include "gstobject.h"
50 #include "gstghostpad.h"
51 #include "gstpad.h"
52 #include "gstutils.h"
53 #include "gstvalue.h"
54
55 /*** PIPELINE GRAPHS **********************************************************/
56
57 const gchar *priv_gst_dump_dot_dir;     /* NULL *//* set from gst.c */
58
59 const gchar spaces[] = {
60   "                                "    /* 32 */
61       "                                "        /* 64 */
62       "                                "        /* 96 */
63       "                                "        /* 128 */
64 };
65
66 extern GstClockTime _priv_gst_info_start_time;
67
68 static gchar *
69 debug_dump_make_object_name (GstObject * element)
70 {
71   return g_strcanon (g_strdup_printf ("%s_%p", GST_OBJECT_NAME (element),
72           element), G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_", '_');
73 }
74
75 static gchar *
76 debug_dump_get_element_state (GstElement * element)
77 {
78   gchar *state_name = NULL;
79   const gchar *state_icons = "~0-=>";
80   GstState state = 0, pending = 0;
81
82   gst_element_get_state (element, &state, &pending, 0);
83   if (pending == GST_STATE_VOID_PENDING) {
84     state_name = g_strdup_printf ("\\n[%c]", state_icons[state]);
85   } else {
86     state_name = g_strdup_printf ("\\n[%c] -> [%c]", state_icons[state],
87         state_icons[pending]);
88   }
89   return state_name;
90 }
91
92 static gchar *
93 debug_dump_get_element_params (GstElement * element)
94 {
95   gchar *param_name = NULL;
96   GParamSpec **properties, *property;
97   GValue value = { 0, };
98   guint i, number_of_properties;
99   gchar *tmp, *value_str;
100
101   /* get paramspecs and show non-default properties */
102   properties =
103       g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
104           (element)), &number_of_properties);
105   if (properties) {
106     for (i = 0; i < number_of_properties; i++) {
107       property = properties[i];
108
109       /* ski some properties */
110       if (!(property->flags & G_PARAM_READABLE))
111         continue;
112       if (!strcmp (property->name, "name"))
113         continue;
114
115       g_value_init (&value, property->value_type);
116       g_object_get_property (G_OBJECT (element), property->name, &value);
117       if (!(g_param_value_defaults (property, &value))) {
118         tmp = g_strdup_value_contents (&value);
119         value_str = g_strescape (tmp, NULL);
120         g_free (tmp);
121         if (param_name) {
122           tmp = param_name;
123           param_name = g_strdup_printf ("%s\\n%s=%s",
124               tmp, property->name, value_str);
125           g_free (tmp);
126         } else {
127           param_name = g_strdup_printf ("\\n%s=%s", property->name, value_str);
128         }
129         g_free (value_str);
130       }
131       g_value_unset (&value);
132     }
133     g_free (properties);
134   }
135   return param_name;
136 }
137
138 static void
139 debug_dump_pad (GstPad * pad, gchar * color_name, gchar * element_name,
140     FILE * out, const gint indent)
141 {
142   GstPadTemplate *pad_templ;
143   GstPadPresence presence;
144   gchar *pad_name, *style_name;
145   gchar pad_flags[6];
146   const gchar *activation_mode = "-><";
147   const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
148
149   pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
150
151   /* pad availability */
152   style_name = "filled,solid";
153   if ((pad_templ = gst_pad_get_pad_template (pad))) {
154     presence = GST_PAD_TEMPLATE_PRESENCE (pad_templ);
155     if (presence == GST_PAD_SOMETIMES) {
156       style_name = "filled,dotted";
157     } else if (presence == GST_PAD_REQUEST) {
158       style_name = "filled,dashed";
159     }
160   }
161   /* check if pad flags */
162   pad_flags[0] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKED) ? 'B' : 'b';
163   pad_flags[1] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLUSHING) ? 'F' : 'f';
164   pad_flags[2] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_IN_GETCAPS) ? 'G' : 'g';
165   pad_flags[3] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_IN_SETCAPS) ? 's' : 's';
166   pad_flags[4] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKING) ? 'B' : 'b';
167   pad_flags[5] = '\0';
168
169   fprintf (out, "%s  %s_%s [color=black, fillcolor=\"%s\", label=\"%s\\n[%c][%s]\", height=\"0.2\", style=\"%s\"];\n", spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad), activation_mode[pad->mode],      /* NONE/PUSH/PULL */
170       pad_flags, style_name);
171
172   g_free (pad_name);
173 }
174
175 static void
176 debug_dump_element_pad (GstPad * pad, GstElement * element,
177     GstDebugGraphDetails details, FILE * out, const gint indent)
178 {
179   GstElement *target_element;
180   GstPad *target_pad, *tmp_pad;
181   GstPadDirection dir;
182   gchar *element_name;
183   gchar *target_element_name;
184   gchar *color_name;
185
186   dir = gst_pad_get_direction (pad);
187   element_name = debug_dump_make_object_name (GST_OBJECT (element));
188   if (GST_IS_GHOST_PAD (pad)) {
189     color_name =
190         (dir == GST_PAD_SRC) ? "#ffdddd" : ((dir ==
191             GST_PAD_SINK) ? "#ddddff" : "#ffffff");
192     /* output target-pad so that it belongs to this element */
193     if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) {
194       if ((target_pad = gst_pad_get_peer (tmp_pad))) {
195         if ((target_element = gst_pad_get_parent_element (target_pad))) {
196           target_element_name =
197               debug_dump_make_object_name (GST_OBJECT (target_element));
198         } else {
199           target_element_name = "";
200         }
201         debug_dump_pad (target_pad, color_name, target_element_name, out,
202             indent);
203         if (target_element) {
204           g_free (target_element_name);
205           gst_object_unref (target_element);
206         }
207         gst_object_unref (target_pad);
208       }
209       gst_object_unref (tmp_pad);
210     }
211   } else {
212     color_name =
213         (dir == GST_PAD_SRC) ? "#ffaaaa" : ((dir ==
214             GST_PAD_SINK) ? "#aaaaff" : "#cccccc");
215   }
216   /* pads */
217   debug_dump_pad (pad, color_name, element_name, out, indent);
218   g_free (element_name);
219 }
220
221 static gboolean
222 string_append_field (GQuark field, const GValue * value, gpointer ptr)
223 {
224   GString *str = (GString *) ptr;
225   gchar *value_str = gst_value_serialize (value);
226
227   /* some enums can become really long */
228   if (strlen (value_str) > 25) {
229     gint pos = 24;
230
231     /* truncate */
232     value_str[25] = '\0';
233
234     /* mirror any brackets */
235     if (value_str[0] == '<')
236       value_str[pos--] = '>';
237     if (value_str[0] == '[')
238       value_str[pos--] = ']';
239     if (value_str[0] == '(')
240       value_str[pos--] = ')';
241     if (value_str[0] == '{')
242       value_str[pos--] = '}';
243     if (pos != 24)
244       value_str[pos--] = ' ';
245     /* elippsize */
246     value_str[pos--] = '.';
247     value_str[pos--] = '.';
248     value_str[pos--] = '.';
249   }
250   g_string_append_printf (str, "  %18s: %s\\l", g_quark_to_string (field),
251       value_str);
252
253   g_free (value_str);
254   return TRUE;
255 }
256
257 static gchar *
258 debug_dump_describe_caps (GstCaps * caps, GstDebugGraphDetails details,
259     gboolean * need_free)
260 {
261   gchar *media = NULL;
262
263   if (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) {
264
265     if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
266       media = gst_caps_to_string (caps);
267       *need_free = TRUE;
268
269     } else {
270       GString *str = NULL;
271       guint i;
272       guint slen = 0;
273
274       for (i = 0; i < gst_caps_get_size (caps); i++) {
275         slen += 25 +
276             STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure (caps, i));
277       }
278
279       str = g_string_sized_new (slen);
280       for (i = 0; i < gst_caps_get_size (caps); i++) {
281         GstStructure *structure = gst_caps_get_structure (caps, i);
282
283         g_string_append (str, gst_structure_get_name (structure));
284         g_string_append (str, "\\l");
285
286         gst_structure_foreach (structure, string_append_field, (gpointer) str);
287       }
288
289       media = g_string_free (str, FALSE);
290       *need_free = TRUE;
291     }
292
293   } else {
294     if (GST_CAPS_IS_SIMPLE (caps))
295       media =
296           (gchar *) gst_structure_get_name (gst_caps_get_structure (caps, 0));
297     else
298       media = "*";
299     *need_free = FALSE;
300   }
301   return media;
302 }
303
304 static void
305 debug_dump_element_pad_link (GstPad * pad, GstElement * element,
306     GstDebugGraphDetails details, FILE * out, const gint indent)
307 {
308   GstElement *peer_element, *target_element;
309   GstPad *peer_pad, *target_pad, *tmp_pad;
310   GstCaps *caps, *peer_caps;
311   gboolean free_caps, free_peer_caps;
312   gboolean free_media, free_media_src, free_media_sink;
313   gchar *media = NULL;
314   gchar *media_src = NULL, *media_sink = NULL;
315   gchar *pad_name, *element_name;
316   gchar *peer_pad_name, *peer_element_name;
317   gchar *target_pad_name, *target_element_name;
318   const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
319
320   if ((peer_pad = gst_pad_get_peer (pad))) {
321     free_media = free_media_src = free_media_sink = FALSE;
322     if ((details & GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE) ||
323         (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS)
324         ) {
325       if ((caps = gst_pad_get_negotiated_caps (pad))) {
326         free_caps = TRUE;
327       } else {
328         free_caps = FALSE;
329         if (!(caps = (GstCaps *)
330                 gst_pad_get_pad_template_caps (pad))) {
331           /* this should not happen */
332           media = "?";
333         }
334       }
335       if ((peer_caps = gst_pad_get_negotiated_caps (peer_pad))) {
336         free_peer_caps = TRUE;
337       } else {
338         free_peer_caps = FALSE;
339         peer_caps = (GstCaps *) gst_pad_get_pad_template_caps (peer_pad);
340       }
341       if (caps) {
342         media = debug_dump_describe_caps (caps, details, &free_media);
343         /* check if peer caps are different */
344         if (peer_caps && !gst_caps_is_equal (caps, peer_caps)) {
345           gchar *tmp;
346           gboolean free_tmp;
347
348           tmp = debug_dump_describe_caps (peer_caps, details, &free_tmp);
349           if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
350             media_src = media;
351             free_media_src = free_media;
352             media_sink = tmp;
353             free_media_sink = free_tmp;
354           } else {
355             media_src = tmp;
356             free_media_src = free_tmp;
357             media_sink = media;
358             free_media_sink = free_media;
359           }
360           media = NULL;
361           free_media = FALSE;
362         }
363         if (free_caps) {
364           gst_caps_unref (caps);
365         }
366       }
367       if (free_peer_caps && peer_caps) {
368         gst_caps_unref (peer_caps);
369       }
370     }
371
372     pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
373     if (element) {
374       element_name = debug_dump_make_object_name (GST_OBJECT (element));
375     } else {
376       element_name = "";
377     }
378     peer_pad_name = debug_dump_make_object_name (GST_OBJECT (peer_pad));
379     if ((peer_element = gst_pad_get_parent_element (peer_pad))) {
380       peer_element_name =
381           debug_dump_make_object_name (GST_OBJECT (peer_element));
382     } else {
383       peer_element_name = "";
384     }
385
386     if (GST_IS_GHOST_PAD (pad)) {
387       if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) {
388         if ((target_pad = gst_pad_get_peer (tmp_pad))) {
389           target_pad_name =
390               debug_dump_make_object_name (GST_OBJECT (target_pad));
391           if ((target_element = gst_pad_get_parent_element (target_pad))) {
392             target_element_name =
393                 debug_dump_make_object_name (GST_OBJECT (target_element));
394           } else {
395             target_element_name = "";
396           }
397           /* src ghostpad relationship */
398           fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc,
399               target_element_name, target_pad_name, element_name, pad_name);
400
401           g_free (target_pad_name);
402           if (target_element) {
403             g_free (target_element_name);
404             gst_object_unref (target_element);
405           }
406           gst_object_unref (target_pad);
407         }
408         gst_object_unref (tmp_pad);
409       }
410     }
411     if (GST_IS_GHOST_PAD (peer_pad)) {
412       if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer_pad)))) {
413         if ((target_pad = gst_pad_get_peer (tmp_pad))) {
414           target_pad_name =
415               debug_dump_make_object_name (GST_OBJECT (target_pad));
416           if ((target_element = gst_pad_get_parent_element (target_pad))) {
417             target_element_name =
418                 debug_dump_make_object_name (GST_OBJECT (target_element));
419           } else {
420             target_element_name = "";
421           }
422           /* sink ghostpad relationship */
423           fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc,
424               peer_element_name, peer_pad_name,
425               target_element_name, target_pad_name);
426           /* FIXME: we are missing links from the proxy pad
427            * theoretically we need to:
428            * pad=gst_object_ref(target_pad);
429            * goto line 280: if ((peer_pad = gst_pad_get_peer (pad)))
430            * as this would be ugly we need to refactor ...
431            */
432           debug_dump_element_pad_link (target_pad, target_element, details, out,
433               indent);
434           g_free (target_pad_name);
435           if (target_element) {
436             g_free (target_element_name);
437             gst_object_unref (target_element);
438           }
439           gst_object_unref (target_pad);
440         }
441         gst_object_unref (tmp_pad);
442       }
443     }
444
445     /* pad link */
446     if (media) {
447       fprintf (out, "%s%s_%s -> %s_%s [label=\"%s\"]\n", spc,
448           element_name, pad_name, peer_element_name, peer_pad_name, media);
449       if (free_media) {
450         g_free (media);
451       }
452     } else if (media_src && media_sink) {
453       /* dot has some issues with placement of head and taillabels,
454        * we need an empty label to make space */
455       fprintf (out, "%s%s_%s -> %s_%s [labeldistance=\"10\", labelangle=\"0\", "
456           "label=\"                                                  \", "
457           "headlabel=\"%s\", taillabel=\"%s\"]\n",
458           spc, element_name, pad_name, peer_element_name, peer_pad_name,
459           media_src, media_sink);
460       if (free_media_src)
461         g_free (media_src);
462       if (free_media_sink)
463         g_free (media_sink);
464     } else {
465       fprintf (out, "%s%s_%s -> %s_%s\n", spc,
466           element_name, pad_name, peer_element_name, peer_pad_name);
467     }
468
469     g_free (pad_name);
470     if (element) {
471       g_free (element_name);
472     }
473     g_free (peer_pad_name);
474     if (peer_element) {
475       g_free (peer_element_name);
476       gst_object_unref (peer_element);
477     }
478     gst_object_unref (peer_pad);
479   }
480 }
481
482 /*
483  * debug_dump_element:
484  * @bin: the bin that should be analyzed
485  * @out: file to write to
486  * @indent: level of graph indentation
487  *
488  * Helper for _gst_debug_bin_to_dot_file() to recursively dump a pipeline.
489  */
490 static void
491 debug_dump_element (GstBin * bin, GstDebugGraphDetails details, FILE * out,
492     const gint indent)
493 {
494   GstIterator *element_iter, *pad_iter;
495   gboolean elements_done, pads_done;
496   GstElement *element;
497   GstPad *pad;
498   GstPadDirection dir;
499   guint src_pads, sink_pads;
500   gchar *element_name;
501   gchar *state_name = NULL;
502   gchar *param_name = NULL;
503   const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
504
505   element_iter = gst_bin_iterate_elements (bin);
506   elements_done = FALSE;
507   while (!elements_done) {
508     switch (gst_iterator_next (element_iter, (gpointer) & element)) {
509       case GST_ITERATOR_OK:
510         element_name = debug_dump_make_object_name (GST_OBJECT (element));
511
512         if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
513           state_name = debug_dump_get_element_state (GST_ELEMENT (element));
514         }
515         if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
516           param_name = debug_dump_get_element_params (GST_ELEMENT (element));
517         }
518         /* elements */
519         fprintf (out, "%ssubgraph cluster_%s {\n", spc, element_name);
520         fprintf (out, "%s  fontname=\"Bitstream Vera Sans\";\n", spc);
521         fprintf (out, "%s  fontsize=\"8\";\n", spc);
522         fprintf (out, "%s  style=filled;\n", spc);
523         fprintf (out, "%s  color=black;\n\n", spc);
524         fprintf (out, "%s  label=\"%s\\n%s%s%s\";\n", spc,
525             G_OBJECT_TYPE_NAME (element), GST_OBJECT_NAME (element),
526             (state_name ? state_name : ""), (param_name ? param_name : "")
527             );
528         if (state_name) {
529           g_free (state_name);
530           state_name = NULL;
531         }
532         if (param_name) {
533           g_free (param_name);
534           param_name = NULL;
535         }
536         g_free (element_name);
537
538         src_pads = sink_pads = 0;
539         if ((pad_iter = gst_element_iterate_pads (element))) {
540           pads_done = FALSE;
541           while (!pads_done) {
542             switch (gst_iterator_next (pad_iter, (gpointer) & pad)) {
543               case GST_ITERATOR_OK:
544                 debug_dump_element_pad (pad, element, details, out, indent);
545                 dir = gst_pad_get_direction (pad);
546                 if (dir == GST_PAD_SRC)
547                   src_pads++;
548                 else if (dir == GST_PAD_SINK)
549                   sink_pads++;
550                 gst_object_unref (pad);
551                 break;
552               case GST_ITERATOR_RESYNC:
553                 gst_iterator_resync (pad_iter);
554                 break;
555               case GST_ITERATOR_ERROR:
556               case GST_ITERATOR_DONE:
557                 pads_done = TRUE;
558                 break;
559             }
560           }
561           gst_iterator_free (pad_iter);
562         }
563         if (GST_IS_BIN (element)) {
564           fprintf (out, "%s  fillcolor=\"#ffffff\";\n", spc);
565           /* recurse */
566           debug_dump_element (GST_BIN (element), details, out, indent + 1);
567         } else {
568           if (src_pads && !sink_pads)
569             fprintf (out, "%s  fillcolor=\"#ffaaaa\";\n", spc);
570           else if (!src_pads && sink_pads)
571             fprintf (out, "%s  fillcolor=\"#aaaaff\";\n", spc);
572           else if (src_pads && sink_pads)
573             fprintf (out, "%s  fillcolor=\"#aaffaa\";\n", spc);
574           else
575             fprintf (out, "%s  fillcolor=\"#ffffff\";\n", spc);
576         }
577         fprintf (out, "%s}\n\n", spc);
578         if ((pad_iter = gst_element_iterate_pads (element))) {
579           pads_done = FALSE;
580           while (!pads_done) {
581             switch (gst_iterator_next (pad_iter, (gpointer) & pad)) {
582               case GST_ITERATOR_OK:
583                 if (gst_pad_is_linked (pad)
584                     && gst_pad_get_direction (pad) == GST_PAD_SRC) {
585                   debug_dump_element_pad_link (pad, element, details, out,
586                       indent);
587                 }
588                 gst_object_unref (pad);
589                 break;
590               case GST_ITERATOR_RESYNC:
591                 gst_iterator_resync (pad_iter);
592                 break;
593               case GST_ITERATOR_ERROR:
594               case GST_ITERATOR_DONE:
595                 pads_done = TRUE;
596                 break;
597             }
598           }
599           gst_iterator_free (pad_iter);
600         }
601         gst_object_unref (element);
602         break;
603       case GST_ITERATOR_RESYNC:
604         gst_iterator_resync (element_iter);
605         break;
606       case GST_ITERATOR_ERROR:
607       case GST_ITERATOR_DONE:
608         elements_done = TRUE;
609         break;
610     }
611   }
612   gst_iterator_free (element_iter);
613 }
614
615 /*
616  * _gst_debug_bin_to_dot_file:
617  * @bin: the top-level pipeline that should be analyzed
618  * @file_name: output base filename (e.g. "myplayer")
619  *
620  * To aid debugging applications one can use this method to write out the whole
621  * network of gstreamer elements that form the pipeline into an dot file.
622  * This file can be processed with graphviz to get an image.
623  * <informalexample><programlisting>
624  *  dot -Tpng -oimage.png graph_lowlevel.dot
625  * </programlisting></informalexample>
626  */
627 void
628 _gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
629     const gchar * file_name)
630 {
631   gchar *full_file_name = NULL;
632   FILE *out;
633
634   g_return_if_fail (GST_IS_BIN (bin));
635
636   if (G_LIKELY (priv_gst_dump_dot_dir == NULL))
637     return;
638
639   if (!file_name) {
640     file_name = g_get_application_name ();
641     if (!file_name)
642       file_name = "unnamed";
643   }
644
645   full_file_name = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.dot",
646       priv_gst_dump_dot_dir, file_name);
647
648   if ((out = fopen (full_file_name, "wb"))) {
649     gchar *state_name = NULL;
650     gchar *param_name = NULL;
651
652     if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
653       state_name = debug_dump_get_element_state (GST_ELEMENT (bin));
654     }
655     if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
656       param_name = debug_dump_get_element_params (GST_ELEMENT (bin));
657     }
658
659     /* write header */
660     fprintf (out,
661         "digraph pipeline {\n"
662         "  rankdir=LR;\n"
663         "  fontname=\"sans\";\n"
664         "  fontsize=\"10\";\n"
665         "  labelloc=t;\n"
666         "  nodesep=.1;\n"
667         "  ranksep=.2;\n"
668         "  label=\"<%s>\\n%s%s%s\";\n"
669         "  node [style=filled, shape=box, fontsize=\"9\", fontname=\"sans\", margin=\"0.0,0.0\"];\n"
670         "  edge [labelfontsize=\"6\", fontsize=\"9\", fontname=\"monospace\"];\n"
671         "\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
672         (state_name ? state_name : ""), (param_name ? param_name : "")
673         );
674     if (state_name)
675       g_free (state_name);
676     if (param_name)
677       g_free (param_name);
678
679     debug_dump_element (bin, details, out, 1);
680
681     /* write footer */
682     fprintf (out, "}\n");
683     fclose (out);
684     GST_INFO ("wrote bin graph to : '%s'", full_file_name);
685   } else {
686     GST_WARNING ("Failed to open file '%s' for writing: %s", full_file_name,
687         g_strerror (errno));
688   }
689   g_free (full_file_name);
690 }
691
692 /*
693  * _gst_debug_bin_to_dot_file_with_ts:
694  * @bin: the top-level pipeline that should be analyzed
695  * @file_name: output base filename (e.g. "myplayer")
696  *
697  * This works like _gst_debug_bin_to_dot_file(), but adds the current timestamp
698  * to the filename, so that it can be used to take multiple snapshots.
699  */
700 void
701 _gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
702     const gchar * file_name)
703 {
704   gchar *ts_file_name = NULL;
705   GstClockTime elapsed;
706
707   g_return_if_fail (GST_IS_BIN (bin));
708
709   if (!file_name) {
710     file_name = g_get_application_name ();
711     if (!file_name)
712       file_name = "unnamed";
713   }
714
715   /* add timestamp */
716   elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
717       gst_util_get_timestamp ());
718
719   /* we don't use GST_TIME_FORMAT as such filenames would fail on some
720    * filesystems like fat */
721   ts_file_name =
722       g_strdup_printf ("%u.%02u.%02u.%09u-%s", GST_TIME_ARGS (elapsed),
723       file_name);
724
725   _gst_debug_bin_to_dot_file (bin, details, ts_file_name);
726   g_free (ts_file_name);
727 }
728 #else /* !GST_DISABLE_GST_DEBUG */
729 #ifndef GST_REMOVE_DISABLED
730 void
731 _gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
732     const gchar * file_name)
733 {
734 }
735
736 void
737 _gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
738     const gchar * file_name)
739 {
740 }
741 #endif /* GST_REMOVE_DISABLED */
742 #endif /* GST_DISABLE_GST_DEBUG */