tools: remove gst-xmlinspect

People should just query the registry themselves or write a small
python script if they need this functionality (which is likely
less work than parsing the XML that this script outputs, and I'm
not aware of anything using the xml2text xsl either).
This commit is contained in:
Tim-Philipp Müller 2011-06-04 14:28:08 +01:00
parent 548856596d
commit 510ee7be34
8 changed files with 3 additions and 1344 deletions

View file

@ -144,13 +144,11 @@ rm -rf $RPM_BUILD_ROOT
%{_bindir}/gst-inspect-%{majorminor}
%{_bindir}/gst-launch-%{majorminor}
%{_bindir}/gst-typefind-%{majorminor}
%{_bindir}/gst-xmlinspect-%{majorminor}
%{_bindir}/gst-xmllaunch-%{majorminor}
%{_libexecdir}/gstreamer-%{majorminor}/gst-plugin-scanner
%doc %{_mandir}/man1/gst-inspect-%{majorminor}.*
%doc %{_mandir}/man1/gst-launch-%{majorminor}.*
%doc %{_mandir}/man1/gst-typefind-%{majorminor}.*
%doc %{_mandir}/man1/gst-xmlinspect-%{majorminor}.*
%doc %{_mandir}/man1/gst-xmllaunch-%{majorminor}.*
%doc %{_datadir}/doc/gstreamer-%{majorminor}/manual
%doc %{_datadir}/doc/gstreamer-%{majorminor}/pwg
@ -171,7 +169,6 @@ rm -rf $RPM_BUILD_ROOT
%{_bindir}/gst-inspect
%{_bindir}/gst-launch
%{_bindir}/gst-typefind
%{_bindir}/gst-xmlinspect
%{_bindir}/gst-xmllaunch
%files devel

2
tools/.gitignore vendored
View file

@ -15,7 +15,6 @@ Makefile.in
gst-inspect
gst-launch
gst-typefind
gst-xmlinspect
gst-xmllaunch
gst-inspect.1
gst-launch.1
@ -25,5 +24,4 @@ gst-xmllaunch.1
gst-inspect-?.?*
gst-launch-?.?*
gst-typefind-?.?*
gst-xmlinspect-?.?*
gst-xmllaunch-?.?*

View file

@ -1 +0,0 @@
gst-launch should not crash with bad options

View file

@ -20,12 +20,10 @@ endif
GST_OTHER_SRC = \
gst-inspect \
gst-typefind \
gst-xmlinspect
gst-typefind
GST_OTHER_SRC_V = \
gst-inspect-@GST_MAJORMINOR@ \
gst-typefind-@GST_MAJORMINOR@ \
gst-xmlinspect-@GST_MAJORMINOR@
gst-typefind-@GST_MAJORMINOR@
### so all of the programs we want to build
bin_PROGRAMS = \
@ -52,9 +50,6 @@ gst_inspect_@GST_MAJORMINOR@_LDADD = $(GST_OBJ_LIBS)
gst_typefind_@GST_MAJORMINOR@_SOURCES = gst-typefind.c tools.h
gst_typefind_@GST_MAJORMINOR@_CFLAGS = $(GST_OBJ_CFLAGS)
gst_typefind_@GST_MAJORMINOR@_LDADD = $(GST_OBJ_LIBS)
gst_xmlinspect_@GST_MAJORMINOR@_SOURCES = gst-xmlinspect.c tools.h
gst_xmlinspect_@GST_MAJORMINOR@_CFLAGS = $(GST_OBJ_CFLAGS)
gst_xmlinspect_@GST_MAJORMINOR@_LDADD = $(GST_OBJ_LIBS)
# make sure each unversioned tool comes from gst-run.c
if !GST_DISABLE_LOADSAVE
@ -67,7 +62,6 @@ endif
gst_inspect_SOURCES = gst-run.c
gst_typefind_SOURCES = gst-run.c
gst_xmlinspect_SOURCES = gst-run.c
# CFLAGS and libs for nonversioned frontend binaries
AM_CFLAGS = $(GLIB_ONLY_CFLAGS)
@ -108,8 +102,7 @@ endif
GST_OTHER_MAN = \
gst-inspect-@GST_MAJORMINOR@.1 \
gst-typefind-@GST_MAJORMINOR@.1 \
gst-xmlinspect-@GST_MAJORMINOR@.1
gst-typefind-@GST_MAJORMINOR@.1
manpages = $(GST_LOADSAVE_MAN) $(GST_PARSE_MAN) $(GST_OTHER_MAN)
@ -126,7 +119,6 @@ EXTRA_DIST = \
gst-inspect.1.in \
gst-launch.1.in \
gst-typefind.1.in \
gst-xmlinspect.1.in \
gst-xmllaunch.1.in \
gst-plot-timeline.py
@ -135,7 +127,6 @@ EXTRA_DIST = \
-e s,gst-inspect,gst-inspect-@GST_MAJORMINOR@,g \
-e s,gst-launch,gst-launch-@GST_MAJORMINOR@,g \
-e s,gst-typefind,gst-typefind-@GST_MAJORMINOR@,g \
-e s,gst-xmlinspect,gst-xmlinspect-@GST_MAJORMINOR@,g \
-e s,gst-xmllaunch,gst-xmllaunch-@GST_MAJORMINOR@,g \
-e s,GST_MAJORMINOR,@GST_MAJORMINOR@,g \
$< >$@

View file

@ -1,134 +0,0 @@
gst-launch
================
This is a tool that will construct pipelines based on a command-line
syntax. The syntax is rather complex to enable all the features I want it
to have, but should be easy to use for most people. Multi-pathed and
feedback pipelines are the most complex.
A simple commandline looks like:
gst-launch filesrc location=music.mp3 ! mad ! osssink
This plays an mp3 music file music.mp3 using libmad, and:
gst-launch filesrc location=music.mp3 ! mp3parse ! mpg123 ! osssink
Plays and mp3 music file using mpg123
You can also stream files over http:
gst-launch httpsrc location=http://domain.com/music.mp3 ! mad ! osssink
And using gnome-vfs you can do the same with:
gst-launch gnomevfssrc location=music.mp3 ! mad ! osssink
gst-launch gnomevfssrc location=http://domain.com/music.mp3 ! mad ! osssink
And too play the same song with gnome-vfs via smb:
gst-launch gnomevfssrc location=smb://computer/music.mp3 ! mad ! osssink
Here we convert a Mp3 file into an Ogg Vorbis file:
gst-launch filesrc location=music.mp3 ! mad ! vorbisenc ! filesink location=music.ogg
And then we can play that file with:
gst-launch filesrc location=music.ogg ! oggdemux ! vorbisdec ! audioconvert ! osssink
Some other useful pipelines are..
Plays wav files (currently there are no wav encoders):
gst-launch filesrc location=music.wav ! wavparse ! osssink
Converts wav files into mp3 and ogg files:
gst-launch filesrc location=music.wav ! wavparse ! vorbisenc ! filesink location=music.ogg
gst-launch filesrc location=music.wav ! wavparse ! mpegaudio ! filesink location=music.mp3
You can also use lame for mp3 encoding if you have it installed, it does a
much better job than mpegaudio.
Rips all songs from cd and saves them into a mp3 file:
gst-launch cdparanoia ! mpegaudio ! filesink location=cd.mp3
You can toy around with gst-inspect to discover the settings for
cdparanoia to rip individual tracks
Record sound from your sound input and encode it into an ogg file:
gst-launch osssrc ! vorbisenc ! filesink location=input.ogg
gst-launch not only handles audio but video as well:
For mpeg1 files (video and audio streams respectively):
gst-launch filesrc location=video.mpg ! mpegdemux video_00! { queue ! mpeg2dec ! sdlvideosink }
gst-launch filesrc location=video.mpg ! mpegdemux audio_00! { queue ! mad ! osssink }
for mpeg1 with both audio and video (for glib2):
gst-launch filesrc location=video.mpg ! mpegdemux name=demux video_00! { queue ! mpeg2dec ! sdlvideosink } demux.audio_00! { queue ! mad ! osssink }
for mpeg1 with both audio and video (for gtk1.2, the shim doesn't handle the 'name' property yet):
gst-launch filesrc location=video.mpg ! mpegdemux video_00! { queue ! mpeg2dec ! sdlvideosink } mpegdemux0.audio_00! { queue ! mad ! osssink }
For mpeg2 files (video and audio streams respectively):
gst-launch filesrc location=video.mpeg ! mpegdemux video_00! { queue ! mpeg2dec ! sdlvideosink }
gst-launch filesrc location=video.mpeg ! mpegdemux private_stream_1.0! { queue ! a52dec ! osssink }
for mpeg2 with both audio and video (glib2):
gst-launch filesrc location=video.mpg ! mpegdemux name=demux video_00! { queue ! mpeg2dec ! sdlvideosink } demux.private_stream_1.0! { queue ! a52dec ! osssink }
Note: The types of audio streams in the mpeg files can vary!
For an avi file (DivX, mjpeg,...)
gst-launch filesrc location=video.avi ! avidecoder video_00! { queue ! sdlvideosink } avidecoder0.audio_00! { queue ! osssink }
gst-complete
==================
This is a simple utility which provides custom bash completion when
typing gst-launch commands.
Simply run "gst-compprep" as root to build the registry of completions,
and then put, in your .bashrc,
"complete -C gst-complete gst-launch"
(ensuring that gst-complete is on your path).
You can then enjoy context sensitive tab-completion of gst-launch
commands.
gst-inspect
=================
Allows you to check the properties of plugins and elements.
./gst-inspect
will show all the plugins available and the elements they contain.
./gst-inspect <pluginname/elementname>
shows more info about the plugin/element.
gst-xmlinspect
=================
Dump properties of plugins and elements in an xml format. You can
transform the xml to something else with an appropriate stylesheet.
./gst-xmlinspect <elementname> | xsltproc xml2text.xsl -

View file

@ -1,51 +0,0 @@
.TH GStreamer 1 "March 2001"
.SH "NAME"
gst\-xmlinspect - print info about a GStreamer plugin or element
.SH "SYNOPSIS"
.B gst\-xmlinspect [OPTION...] [PLUGIN|ELEMENT]
.SH "DESCRIPTION"
.PP
\fIgst\-xmlinspect\fP is a tool that prints out information on
available \fIGStreamer\fP plugins, information about a particular
plugin, or information about a particular element. When executed
with no PLUGIN or ELEMENT argument, \fIgst\-xmlinspect\fP will print
a list of all plugins and elements. When executed with a PLUGIN
or ELEMENT argument, \fIgst\-xmlinspect\fP will print information
about that plug-in or element as an XML document.
.
.SH "OPTIONS"
.l
\fIgst\-xmlinspect\fP accepts the following arguments and options:
.TP 8
.B PLUGIN
Name of a plugin
.TP 8
.B ELEMENT
Name of an element
.TP 8
.B \-\-help
Print help synopsis and available FLAGS
.TP 8
.B \-\-gst\-info\-mask=FLAGS
\fIGStreamer\fP info flags to set (list with \-\-help)
.TP 8
.B \-\-gst\-debug\-mask=FLAGS
\fIGStreamer\fP debugging flags to set (list with \-\-help)
.TP 8
.B \-\-gst\-mask=FLAGS
\fIGStreamer\fP info and debugging flags to set (list with \-\-help)
.TP 8
.B \-\-gst\-plugin\-spew
\fIGStreamer\fP info flags to set
Enable printout of errors while loading \fIGStreamer\fP plugins
.TP 8
.B \-\-gst\-plugin\-path=PATH
Add directories separated with ':' to the plugin search path
.
.SH "SEE ALSO"
.BR gst\-feedback (1),
.BR gst\-inspect (1),
.BR gst\-launch (1),
.BR gst\-typefind (1)
.SH "AUTHOR"
The GStreamer team at http://gstreamer.freedesktop.org/

View file

@ -1,685 +0,0 @@
/* GStreamer gst-xmlinspect
* Copyright (C) 2003 Wim Taymans <wtay@chello.be>
*
* 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.
*/
/* FIXME 0.11: remove gst-xmlinspect and gst-feedback etc. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <locale.h>
#include <glib/gprintf.h>
#include "tools.h"
#define PUT_START_TAG(pfx,tag) \
G_STMT_START{ \
g_print ("%*.*s<%s>\n", pfx, pfx, "", tag); \
}G_STMT_END
#define PUT_END_TAG(pfx,tag) \
G_STMT_START{ \
g_print ("%*.*s</%s>\n", pfx, pfx, "", tag); \
}G_STMT_END
#define PUT_ESCAPED(pfx,tag,value) \
G_STMT_START{ \
const gchar *toconv = value; \
if (value) { \
gchar *v = g_markup_escape_text (toconv, strlen (toconv)); \
g_print ("%*.*s<%s>%s</%s>\n", pfx, pfx, "", tag, v, tag); \
g_free (v); \
} \
}G_STMT_END
#ifdef G_HAVE_ISO_VARARGS
#define PUT_STRING(pfx, ...) \
G_STMT_START{ \
gchar *ps_val = g_strdup_printf(__VA_ARGS__); \
g_print ("%*.*s%s\n", pfx, pfx, "", ps_val); \
g_free(ps_val); \
}G_STMT_END
#elif defined(G_HAVE_GNUC_VARARGS)
#define PUT_STRING(pfx, str, a...) \
G_STMT_START{ \
g_print ("%*.*s"str"\n", pfx, pfx, "" , ##a); \
}G_STMT_END
#else
static inline void
PUT_STRING (int pfx, const char *format, ...)
{
va_list varargs;
g_print ("%*.*s", pfx, pfx, "");
va_start (varargs, format);
g_vprintf (format, varargs);
va_end (varargs);
g_print ("\n");
}
#endif
static void
print_caps (const GstCaps * caps, gint pfx)
{
char *s;
if (!caps)
return;
s = gst_caps_to_string (caps);
PUT_ESCAPED (pfx, "caps", s);
g_free (s);
}
#if 0
static void
print_formats (const GstFormat * formats, gint pfx)
{
while (formats && *formats) {
const GstFormatDefinition *definition;
definition = gst_format_get_details (*formats);
if (definition)
PUT_STRING (pfx, "<format id=\"%d\" nick=\"%s\">%s</format>",
*formats, definition->nick, definition->description);
else
PUT_STRING (pfx, "<format id=\"%d\">unknown</format>", *formats);
formats++;
}
}
#endif
static void
print_query_types (const GstQueryType * types, gint pfx)
{
while (types && *types) {
const GstQueryTypeDefinition *definition;
definition = gst_query_type_get_details (*types);
if (definition)
PUT_STRING (pfx, "<query-type id=\"%d\" nick=\"%s\">%s</query-type>",
*types, definition->nick, definition->description);
else
PUT_STRING (pfx, "<query-type id=\"%d\">unknown</query-type>", *types);
types++;
}
}
#if 0
static void
print_event_masks (const GstEventMask * masks, gint pfx)
{
GType event_type;
GEnumClass *klass;
GType event_flags;
GFlagsClass *flags_class = NULL;
event_type = gst_event_type_get_type ();
klass = (GEnumClass *) g_type_class_ref (event_type);
while (masks && masks->type) {
GEnumValue *value;
gint flags = 0, index = 0;
switch (masks->type) {
case GST_EVENT_SEEK:
flags = masks->flags;
event_flags = gst_seek_type_get_type ();
flags_class = (GFlagsClass *) g_type_class_ref (event_flags);
break;
default:
break;
}
value = g_enum_get_value (klass, masks->type);
PUT_STRING (pfx, "<event type=\"%s\">", value->value_nick);
while (flags) {
GFlagsValue *value;
if (flags & 1) {
value = g_flags_get_first_value (flags_class, 1 << index);
if (value)
PUT_ESCAPED (pfx + 1, "flag", value->value_nick);
else
PUT_ESCAPED (pfx + 1, "flag", "?");
}
flags >>= 1;
index++;
}
PUT_END_TAG (pfx, "event");
masks++;
}
}
#endif
static void
output_hierarchy (GType type, gint level, gint * maxlevel)
{
GType parent;
parent = g_type_parent (type);
*maxlevel = *maxlevel + 1;
level++;
PUT_STRING (level, "<object name=\"%s\">", g_type_name (type));
if (parent)
output_hierarchy (parent, level, maxlevel);
PUT_END_TAG (level, "object");
}
static void
print_element_properties (GstElement * element, gint pfx)
{
GParamSpec **property_specs;
guint num_properties;
gint i;
gboolean readable;
property_specs = g_object_class_list_properties
(G_OBJECT_GET_CLASS (element), &num_properties);
PUT_START_TAG (pfx, "element-properties");
for (i = 0; i < num_properties; i++) {
GValue value = { 0, };
GParamSpec *param = property_specs[i];
readable = FALSE;
g_value_init (&value, param->value_type);
if (param->flags & G_PARAM_READABLE) {
g_object_get_property (G_OBJECT (element), param->name, &value);
readable = TRUE;
}
PUT_START_TAG (pfx + 1, "element-property");
PUT_ESCAPED (pfx + 2, "name", g_param_spec_get_name (param));
PUT_ESCAPED (pfx + 2, "type", g_type_name (param->value_type));
PUT_ESCAPED (pfx + 2, "nick", g_param_spec_get_nick (param));
PUT_ESCAPED (pfx + 2, "blurb", g_param_spec_get_blurb (param));
if (readable) {
PUT_ESCAPED (pfx + 2, "flags", "RW");
} else {
PUT_ESCAPED (pfx + 2, "flags", "W");
}
switch (G_VALUE_TYPE (&value)) {
case G_TYPE_STRING:
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
case G_TYPE_BOOLEAN:
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
case G_TYPE_ULONG:
{
GParamSpecULong *pulong = G_PARAM_SPEC_ULONG (param);
PUT_STRING (pfx + 2, "<range min=\"%lu\" max=\"%lu\"/>",
pulong->minimum, pulong->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_LONG:
{
GParamSpecLong *plong = G_PARAM_SPEC_LONG (param);
PUT_STRING (pfx + 2, "<range min=\"%ld\" max=\"%ld\"/>",
plong->minimum, plong->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_UINT:
{
GParamSpecUInt *puint = G_PARAM_SPEC_UINT (param);
PUT_STRING (pfx + 2, "<range min=\"%u\" max=\"%u\"/>",
puint->minimum, puint->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_INT:
{
GParamSpecInt *pint = G_PARAM_SPEC_INT (param);
PUT_STRING (pfx + 2, "<range min=\"%d\" max=\"%d\"/>",
pint->minimum, pint->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_UINT64:
{
GParamSpecUInt64 *puint64 = G_PARAM_SPEC_UINT64 (param);
PUT_STRING (pfx + 2,
"<range min=\"%" G_GUINT64_FORMAT "\" max=\"%" G_GUINT64_FORMAT
"\"/>", puint64->minimum, puint64->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_INT64:
{
GParamSpecInt64 *pint64 = G_PARAM_SPEC_INT64 (param);
PUT_STRING (pfx + 2,
"<range min=\"%" G_GINT64_FORMAT "\" max=\"%" G_GINT64_FORMAT
"\"/>", pint64->minimum, pint64->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_FLOAT:
{
GParamSpecFloat *pfloat = G_PARAM_SPEC_FLOAT (param);
PUT_STRING (pfx + 2, "<range min=\"%f\" max=\"%f\"/>",
pfloat->minimum, pfloat->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
case G_TYPE_DOUBLE:
{
GParamSpecDouble *pdouble = G_PARAM_SPEC_DOUBLE (param);
PUT_STRING (pfx + 2, "<range min=\"%g\" max=\"%g\"/>",
pdouble->minimum, pdouble->maximum);
PUT_ESCAPED (pfx + 2, "default", g_strdup_value_contents (&value));
break;
}
default:
if (param->value_type == GST_TYPE_CAPS) {
GstCaps *caps = g_value_peek_pointer (&value);
if (!caps)
PUT_ESCAPED (pfx + 2, "default", "NULL");
else {
print_caps (caps, 2);
}
} else if (G_IS_PARAM_SPEC_ENUM (param)) {
GEnumValue *values;
guint j = 0;
gint enum_value;
values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values;
enum_value = g_value_get_enum (&value);
while (values[j].value_name) {
if (values[j].value == enum_value)
break;
j++;
}
PUT_STRING (pfx + 2, "<default>%d</default>", values[j].value);
PUT_START_TAG (pfx + 2, "enum-values");
j = 0;
while (values[j].value_name) {
PUT_STRING (pfx + 3, "<value value=\"%d\" nick=\"%s\"/>",
values[j].value, values[j].value_nick);
j++;
}
PUT_END_TAG (pfx + 2, "enum-values");
} else if (G_IS_PARAM_SPEC_FLAGS (param)) {
GFlagsValue *values;
guint j = 0;
gint flags_value;
values = G_FLAGS_CLASS (g_type_class_ref (param->value_type))->values;
flags_value = g_value_get_flags (&value);
PUT_STRING (pfx + 2, "<default>%d</default>", flags_value);
PUT_START_TAG (pfx + 2, "flags");
j = 0;
while (values[j].value_name) {
PUT_STRING (pfx + 3, "<flag value=\"%d\" nick=\"%s\"/>",
values[j].value, values[j].value_nick);
j++;
}
PUT_END_TAG (pfx + 2, "flags");
} else if (G_IS_PARAM_SPEC_OBJECT (param)) {
PUT_ESCAPED (pfx + 2, "object-type", g_type_name (param->value_type));
}
break;
}
PUT_END_TAG (pfx + 1, "element-property");
}
PUT_END_TAG (pfx, "element-properties");
g_free (property_specs);
}
static void
print_element_signals (GstElement * element, gint pfx)
{
guint *signals;
guint nsignals;
gint i, k;
GSignalQuery *query;
signals = g_signal_list_ids (G_OBJECT_TYPE (element), &nsignals);
for (k = 0; k < 2; k++) {
gint counted = 0;
if (k == 0)
PUT_START_TAG (pfx, "element-signals");
else
PUT_START_TAG (pfx, "element-actions");
for (i = 0; i < nsignals; i++) {
gint n_params;
GType return_type;
const GType *param_types;
gint j;
query = g_new0 (GSignalQuery, 1);
g_signal_query (signals[i], query);
if ((k == 0 && !(query->signal_flags & G_SIGNAL_ACTION)) ||
(k == 1 && (query->signal_flags & G_SIGNAL_ACTION))) {
n_params = query->n_params;
return_type = query->return_type;
param_types = query->param_types;
PUT_START_TAG (pfx + 1, "signal");
PUT_ESCAPED (pfx + 2, "name", query->signal_name);
PUT_ESCAPED (pfx + 2, "return-type", g_type_name (return_type));
PUT_ESCAPED (pfx + 2, "object-type",
g_type_name (G_OBJECT_TYPE (element)));
PUT_START_TAG (pfx + 2, "params");
for (j = 0; j < n_params; j++) {
PUT_ESCAPED (pfx + 3, "type", g_type_name (param_types[j]));
}
PUT_END_TAG (pfx + 2, "params");
PUT_END_TAG (pfx + 1, "signal");
counted++;
}
g_free (query);
}
if (k == 0)
PUT_END_TAG (pfx, "element-signals");
else
PUT_END_TAG (pfx, "element-actions");
}
}
static gboolean
put_factory_metadata (GQuark field_id, const GValue * value, gpointer user_data)
{
const gchar *key = g_quark_to_string (field_id);
const gchar *val = g_value_get_string (value);
PUT_ESCAPED (2, key, val);
return TRUE;
}
static gint
print_element_info (GstElementFactory * factory)
{
GstElement *element;
GstElementClass *gstelement_class;
GList *pads;
GstPad *pad;
GstStaticPadTemplate *padtemplate;
gint maxlevel = 0;
element = gst_element_factory_create (factory, "element");
if (!element) {
g_print ("couldn't construct element for some reason\n");
return -1;
}
PUT_START_TAG (0, "element");
PUT_ESCAPED (1, "name", GST_OBJECT_NAME (factory));
gstelement_class = GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element));
PUT_START_TAG (1, "details");
gst_structure_foreach ((GstStructure *) factory->metadata,
put_factory_metadata, NULL);
PUT_END_TAG (1, "details");
output_hierarchy (G_OBJECT_TYPE (element), 0, &maxlevel);
PUT_START_TAG (1, "pad-templates");
if (factory->numpadtemplates) {
pads = factory->staticpadtemplates;
while (pads) {
padtemplate = (GstStaticPadTemplate *) (pads->data);
pads = g_list_next (pads);
PUT_START_TAG (2, "pad-template");
PUT_ESCAPED (3, "name", padtemplate->name_template);
if (padtemplate->direction == GST_PAD_SRC)
PUT_ESCAPED (3, "direction", "src");
else if (padtemplate->direction == GST_PAD_SINK)
PUT_ESCAPED (3, "direction", "sink");
else
PUT_ESCAPED (3, "direction", "unknown");
if (padtemplate->presence == GST_PAD_ALWAYS)
PUT_ESCAPED (3, "presence", "always");
else if (padtemplate->presence == GST_PAD_SOMETIMES)
PUT_ESCAPED (3, "presence", "sometimes");
else if (padtemplate->presence == GST_PAD_REQUEST) {
PUT_ESCAPED (3, "presence", "request");
PUT_ESCAPED (3, "request-function",
GST_DEBUG_FUNCPTR_NAME (gstelement_class->request_new_pad));
} else
PUT_ESCAPED (3, "presence", "unknown");
if (padtemplate->static_caps.string) {
print_caps (gst_static_caps_get (&padtemplate->static_caps), 3);
}
PUT_END_TAG (2, "pad-template");
}
}
PUT_END_TAG (1, "pad-templates");
PUT_START_TAG (1, "element-flags");
PUT_END_TAG (1, "element-flags");
if (GST_IS_BIN (element)) {
PUT_START_TAG (1, "bin-flags");
PUT_END_TAG (1, "bin-flags");
}
PUT_START_TAG (1, "element-implementation");
PUT_STRING (2, "<state-change function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state));
PUT_END_TAG (1, "element-implementation");
PUT_START_TAG (1, "clocking-interaction");
if (gst_element_requires_clock (element)) {
PUT_STRING (2, "<requires-clock/>");
}
if (gst_element_provides_clock (element)) {
GstClock *clock;
clock = gst_element_get_clock (element);
if (clock)
PUT_STRING (2, "<provides-clock name=\"%s\"/>", GST_OBJECT_NAME (clock));
}
PUT_END_TAG (1, "clocking-interaction");
if (gst_element_is_indexable (element)) {
PUT_STRING (1, "<indexing-capabilities/>");
}
PUT_START_TAG (1, "pads");
if (element->numpads) {
const GList *pads;
pads = element->pads;
while (pads) {
GstCaps *caps;
pad = GST_PAD (pads->data);
pads = g_list_next (pads);
PUT_START_TAG (2, "pad");
PUT_ESCAPED (3, "name", gst_pad_get_name (pad));
if (gst_pad_get_direction (pad) == GST_PAD_SRC)
PUT_ESCAPED (3, "direction", "src");
else if (gst_pad_get_direction (pad) == GST_PAD_SINK)
PUT_ESCAPED (3, "direction", "sink");
else
PUT_ESCAPED (3, "direction", "unknown");
if (pad->padtemplate)
PUT_ESCAPED (3, "template", pad->padtemplate->name_template);
PUT_START_TAG (3, "implementation");
if (pad->chainfunc)
PUT_STRING (4, "<chain-based function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (pad->chainfunc));
if (pad->getrangefunc)
PUT_STRING (4, "<get-range-based function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (pad->getrangefunc));
if (pad->eventfunc != gst_pad_event_default)
PUT_STRING (4, "<event-function function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (pad->eventfunc));
if (pad->queryfunc != gst_pad_query_default)
PUT_STRING (4, "<query-function function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (pad->queryfunc));
if (pad->querytypefunc != gst_pad_get_query_types_default) {
PUT_STRING (4, "<query-type-func function=\"%s\">",
GST_DEBUG_FUNCPTR_NAME (pad->querytypefunc));
print_query_types (gst_pad_get_query_types (pad), 5);
PUT_END_TAG (4, "query-type-func");
}
if (pad->iterintlinkfunc != gst_pad_iterate_internal_links_default)
PUT_STRING (4, "<iterintlink-function function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (pad->iterintlinkfunc));
PUT_END_TAG (3, "implementation");
caps = gst_pad_get_current_caps (pad);
if (caps) {
print_caps (caps, 3);
gst_caps_unref (caps);
}
PUT_END_TAG (2, "pad");
}
}
PUT_END_TAG (1, "pads");
print_element_properties (element, 1);
print_element_signals (element, 1);
/* for compound elements */
/* FIXME: gst_bin_get_list does not exist anymore
if (GST_IS_BIN (element)) {
GList *children;
GstElement *child;
PUT_START_TAG (1, "children");
children = (GList *) gst_bin_get_list (GST_BIN (element));
while (children) {
child = GST_ELEMENT (children->data);
children = g_list_next (children);
PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child));
}
PUT_END_TAG (1, "children");
}
*/
PUT_END_TAG (0, "element");
return 0;
}
int
main (int argc, char *argv[])
{
GstElementFactory *factory;
GOptionEntry options[] = {
GST_TOOLS_GOPTION_VERSION,
{NULL}
};
GOptionContext *ctx;
GError *err = NULL;
setlocale (LC_ALL, "");
g_thread_init (NULL);
gst_tools_set_prgname ("gst-xmlinspect");
ctx = g_option_context_new ("[ELEMENT-NAME]");
g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
g_option_context_add_group (ctx, gst_init_get_option_group ());
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
g_print ("Error initializing: %s\n", err->message);
exit (1);
}
g_option_context_free (ctx);
gst_tools_print_version ("gst-xmlinspect");
/* if no arguments, print out all elements */
if (argc == 1) {
GList *features, *f;
features = gst_registry_get_feature_list (gst_registry_get_default (),
GST_TYPE_ELEMENT_FACTORY);
PUT_STRING (0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
for (f = features; f != NULL; f = f->next)
print_element_info (GST_ELEMENT_FACTORY (f->data));
gst_plugin_feature_list_free (features);
return 0;
}
/* else we try to get a factory */
factory = gst_element_factory_find (argv[1]);
/* if there's a factory, print out the info */
if (factory) {
PUT_STRING (0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
return print_element_info (factory);
}
/* otherwise, error out */
g_printerr ("no such element '%s'\n", argv[1]);
return -1;
}

View file

@ -1,456 +0,0 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="us-ascii" omit-xml-declaration="yes" indent="no"/>
<xsl:variable name="padding" select="string(' ')"/>
<xsl:template match="/element">
<xsl:apply-templates select="name"/>
<xsl:apply-templates select="details"/>
<xsl:apply-templates select="object"/>
<xsl:apply-templates select="pad-templates"/>
<xsl:apply-templates select="element-flags"/>
<xsl:apply-templates select="element-implementation"/>
<xsl:apply-templates select="clocking-interaction"/>
<xsl:apply-templates select="indexing-capabilities"/>
<xsl:apply-templates select="pads"/>
<xsl:apply-templates select="element-properties"/>
<xsl:apply-templates select="dyn-params"/>
<xsl:apply-templates select="element-signals"/>
<xsl:apply-templates select="element-actions"/>
</xsl:template>
<xsl:template match="name">
<xsl:text>Element Name: </xsl:text><xsl:value-of select="."/>
<xsl:text>&#10;&#10;</xsl:text>
</xsl:template>
<xsl:template match="details">
<xsl:text>Factory Details:&#10;</xsl:text>
<xsl:text> Long Name:&#9;</xsl:text> <xsl:value-of select="long-name"/> <xsl:text>&#10;</xsl:text>
<xsl:text> Class:&#9;</xsl:text> <xsl:value-of select="class"/> <xsl:text>&#10;</xsl:text>
<xsl:text> License:&#9;</xsl:text> <xsl:value-of select="license"/> <xsl:text>&#10;</xsl:text>
<xsl:text> Description:&#9;</xsl:text> <xsl:value-of select="description"/> <xsl:text>&#10;</xsl:text>
<xsl:text> Version:&#9;</xsl:text> <xsl:value-of select="version"/> <xsl:text>&#10;</xsl:text>
<xsl:text> Author(s):&#9;</xsl:text> <xsl:value-of select="authors"/> <xsl:text>&#10;</xsl:text>
<xsl:text> Copyright:&#9;</xsl:text> <xsl:value-of select="copyright"/> <xsl:text>&#10;</xsl:text>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template name="object">
<xsl:param name="i"/>
<xsl:param name="j"/>
<xsl:if test="count($i/*) &gt; 0">
<xsl:call-template name="object">
<xsl:with-param name="i" select="$i/object"/>
<xsl:with-param name="j" select="$j - 1"/>
</xsl:call-template>
<xsl:value-of select="substring ($padding, 1, $j * 6)"/>
<xsl:text> +----</xsl:text>
</xsl:if>
<xsl:value-of select="$i/@name"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="object">
<xsl:call-template name="object">
<xsl:with-param name="i" select="."/>
<xsl:with-param name="j" select="count(.//object[(*)])"/>
</xsl:call-template>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="pad-templates">
<xsl:text>Pad Templates&#10;</xsl:text>
<xsl:apply-templates select="./pad-template"/>
</xsl:template>
<xsl:template match="pad-template">
<xsl:text> </xsl:text>
<xsl:value-of select="direction"/>
<xsl:text> template: </xsl:text>
<xsl:value-of select="name"/>
<xsl:text>&#10;</xsl:text>
<xsl:text> Availability: </xsl:text> <xsl:value-of select="presence"/>
<xsl:text>&#10;</xsl:text>
<xsl:text> Capabilities:&#10; </xsl:text> <xsl:apply-templates select="./capscomp"/>
</xsl:template>
<xsl:template match="capscomp">
<xsl:apply-templates select="./caps"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="caps">
<xsl:text> '</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>'&#10;</xsl:text>
<xsl:text> MIME type: </xsl:text>
<xsl:value-of select="type"/>
<xsl:text>'&#10;</xsl:text>
<xsl:apply-templates select="./properties"/>
</xsl:template>
<xsl:template match="properties">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="list">
<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>&#9;:List&#10;</xsl:text>
<xsl:apply-templates select="*" mode="list"/>
</xsl:template>
<!-- propety entries in list mode -->
<xsl:template match="string" mode="list">
<xsl:text> String: '</xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>'&#10;</xsl:text>
</xsl:template>
<xsl:template match="fourcc" mode="list">
<xsl:text> FourCC: '</xsl:text>
<xsl:value-of select="@hexvalue"/>
<xsl:text>'&#10;</xsl:text>
</xsl:template>
<xsl:template match="int" mode="list">
<xsl:text> Integer: </xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="range" mode="list">
<xsl:text> Integer range: </xsl:text>
<xsl:value-of select="concat(@min, ' - ', @max)"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="float" mode="list">
<xsl:text> Float: </xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="floatrange" mode="list">
<xsl:text> Float range: </xsl:text>
<xsl:value-of select="concat(@min, ' - ', @max)"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<!-- propety entries in normal mode -->
<xsl:template match="string">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : String: '</xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>'&#10;</xsl:text>
</xsl:template>
<xsl:template match="fourcc">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : FourCC: '</xsl:text>
<xsl:value-of select="@hexvalue"/>
<xsl:text>'&#10;</xsl:text>
</xsl:template>
<xsl:template match="int">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : Integer: </xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="range">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : Integer range: </xsl:text>
<xsl:value-of select="concat(@min, ' - ', @max)"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="float">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : Float: </xsl:text>
<xsl:value-of select="@value"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="floatrange">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat (@name, $padding), 1, 15)"/>
<xsl:text> : Float range: </xsl:text>
<xsl:value-of select="concat(@min, ' - ', @max)"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="flag">
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="element-flags">
<xsl:text>Element Flags:&#10;</xsl:text>
<xsl:apply-templates select="./flag"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="state-change">
<xsl:text> Has change_state() function: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="load">
<xsl:text> Has custom restore_thyself() function: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="save">
<xsl:text> Has custom save_thyself() function: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="element-implementation">
<xsl:text>Element Implementation:&#10;</xsl:text>
<xsl:apply-templates select="*"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="requires-clock">
<xsl:text> element requires a clock&#10;</xsl:text>
</xsl:template>
<xsl:template match="provides-clock">
<xsl:text> element provides a clock: </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="clocking-interaction">
<xsl:text>Clocking Interaction:&#10;</xsl:text>
<xsl:choose>
<xsl:when test="count(*) = 0">
<xsl:text> none&#10;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="*"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="indexing-capabilities">
<xsl:text> element can do indexing</xsl:text>
</xsl:template>
<xsl:template match="dyn-params">
<xsl:text>Dynamic Parameters:&#10;</xsl:text>
<xsl:choose>
<xsl:when test="count(*) = 0">
<xsl:text> none&#10;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="dyn-param"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="pads">
<xsl:text>Pads:&#10;</xsl:text>
<xsl:apply-templates select="pad"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="pad">
<xsl:text> </xsl:text>
<xsl:value-of select="direction"/>
<xsl:text>: '</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>'&#10;</xsl:text>
<xsl:apply-templates select="implementation"/>
<xsl:text> Pad Template: '</xsl:text>
<xsl:value-of select="template"/>
<xsl:text>'&#10;</xsl:text>
</xsl:template>
<xsl:template match="implementation">
<xsl:text> Implementation:&#10;</xsl:text>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="chain-based">
<xsl:text> Has chainfunc(): </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="bufferpool-function">
<xsl:text> Has bufferpoolfunc(): </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="format">
<xsl:text> (</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>)&#9;</xsl:text>
<xsl:value-of select="@nick"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="."/>
<xsl:text>)&#10;</xsl:text>
</xsl:template>
<xsl:template match="formats-function">
<xsl:text> Supports seeking/conversion/query formats: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="format"/>
</xsl:template>
<xsl:template match="convert-function">
<xsl:text> Has custom convertfunc(): </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="query-function">
<xsl:text> Has custom queryfunc(): </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="event-function">
<xsl:text> Has custom eventfunc(): </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="event">
<xsl:text> </xsl:text>
<xsl:value-of select="@type"/>
<xsl:for-each select="flag">
<xsl:text> | </xsl:text>
<xsl:value-of select='.'/>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="event-mask-func">
<xsl:text> Provides event masks: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="event"/>
</xsl:template>
<xsl:template match="query-type">
<xsl:text> (</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>)&#9;</xsl:text>
<xsl:value-of select="@nick"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="."/>
<xsl:text>)&#10;</xsl:text>
</xsl:template>
<xsl:template match="query-type-func">
<xsl:text> Provides query types: </xsl:text>
<xsl:value-of select="@function"/>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="query-type"/>
</xsl:template>
<xsl:template match="element-properties">
<xsl:text>Element Arguments:&#10;</xsl:text>
<xsl:apply-templates select="element-property"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="default">
<xsl:text>. (Default </xsl:text>
<xsl:value-of select="."/>
<xsl:text>)</xsl:text>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="range" mode="params">
<xsl:value-of select="substring ($padding, 1, 25)"/>
<xsl:text>Range : </xsl:text>
<xsl:value-of select="concat(@min, ' - ', @max)"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="element-property|dyn-param">
<xsl:text> </xsl:text>
<xsl:value-of select="substring (concat(name, $padding), 1, 20)"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="blurb"/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="substring ($padding, 1, 25)"/>
<xsl:value-of select="type"/>
<xsl:apply-templates select="default"/>
<xsl:apply-templates select="range" mode="params"/>
</xsl:template>
<xsl:template match="params">
<xsl:for-each select="type">
<xsl:text>,&#10;</xsl:text>
<xsl:value-of select="substring ($padding, 1, 25)"/>
<xsl:value-of select="substring ($padding, 1, 20)"/>
<xsl:value-of select="."/>
<xsl:text> arg</xsl:text>
<xsl:value-of select="position()"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="signal">
<xsl:value-of select="substring (concat('&quot;', name, '&quot;', $padding), 1, 25)"/>
<xsl:value-of select="return-type"/>
<xsl:text> user_function </xsl:text>
<xsl:value-of select="concat ('(', object-type, '* object')"/>
<xsl:apply-templates select="params"/>
</xsl:template>
<xsl:template match="element-signals">
<xsl:text>Element Signals:&#10;</xsl:text>
<xsl:choose>
<xsl:when test="count(*) = 0">
<xsl:text> none&#10;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="signal">
<xsl:apply-templates select="."/>
<xsl:text>,&#10;</xsl:text>
<xsl:value-of select="substring ($padding, 1, 25)"/>
<xsl:value-of select="substring ($padding, 1, 20)"/>
<xsl:text>gpointer user_data);&#10;</xsl:text>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="element-actions">
<xsl:text>Element Actions:&#10;</xsl:text>
<xsl:choose>
<xsl:when test="count(*) = 0">
<xsl:text> none&#10;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="signal">
<xsl:apply-templates select="."/>
<xsl:text>);&#10;</xsl:text>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>