summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>2005-07-01 12:43:03 +0000
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>2005-07-01 12:43:03 +0000
commitc9a37cf682395ac7c7ae0dabce74f9e37a894fc8 (patch)
treea8b858f6ed6681d6aada3660b2b1c6f6bc907c3b
parentedcaf2cd4f4699f3f10869717b799081ad740f10 (diff)
Enable building the PWG examples.
Original commit message from CVS: * configure.ac: * examples/Makefile.am: * examples/pwg/Makefile.am: * examples/pwg/extract.pl: Enable building the PWG examples. * docs/pwg/advanced-interfaces.xml: Add URI interface stub. * docs/pwg/advanced-types.xml: * docs/pwg/other-autoplugger.xml: * docs/pwg/appendix-porting.xml: * docs/pwg/pwg.xml: Add porting guide (mostly stubs), remove autoplugging (see ADM). * docs/pwg/building-boiler.xml: * docs/pwg/building-chainfn.xml: * docs/pwg/building-pads.xml: * docs/pwg/building-props.xml: * docs/pwg/building-state.xml: * docs/pwg/building-testapp.xml: Update the building-*.xml parts for 0.9 changes. All examples code blocks compile in examples/pwg/*.
-rw-r--r--ChangeLog23
-rw-r--r--configure.ac1
-rw-r--r--docs/pwg/advanced-interfaces.xml7
-rw-r--r--docs/pwg/advanced-types.xml4
-rw-r--r--docs/pwg/appendix-porting.xml37
-rw-r--r--docs/pwg/building-boiler.xml122
-rw-r--r--docs/pwg/building-chainfn.xml118
-rw-r--r--docs/pwg/building-pads.xml149
-rw-r--r--docs/pwg/building-props.xml19
-rw-r--r--docs/pwg/building-state.xml38
-rw-r--r--docs/pwg/building-testapp.xml54
-rw-r--r--docs/pwg/other-autoplugger.xml9
-rw-r--r--docs/pwg/pwg.xml4
-rw-r--r--examples/Makefile.am1
-rw-r--r--examples/pwg/Makefile.am66
-rwxr-xr-xexamples/pwg/extract.pl78
-rw-r--r--tests/old/examples/Makefile.am1
-rw-r--r--tests/old/examples/pwg/Makefile.am66
-rwxr-xr-xtests/old/examples/pwg/extract.pl78
19 files changed, 720 insertions, 155 deletions
diff --git a/ChangeLog b/ChangeLog
index 5a7eb24b0..6f67bdf49 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2005-07-01 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+
+ * configure.ac:
+ * examples/Makefile.am:
+ * examples/pwg/Makefile.am:
+ * examples/pwg/extract.pl:
+ Enable building the PWG examples.
+ * docs/pwg/advanced-interfaces.xml:
+ Add URI interface stub.
+ * docs/pwg/advanced-types.xml:
+ * docs/pwg/other-autoplugger.xml:
+ * docs/pwg/appendix-porting.xml:
+ * docs/pwg/pwg.xml:
+ Add porting guide (mostly stubs), remove autoplugging (see ADM).
+ * docs/pwg/building-boiler.xml:
+ * docs/pwg/building-chainfn.xml:
+ * docs/pwg/building-pads.xml:
+ * docs/pwg/building-props.xml:
+ * docs/pwg/building-state.xml:
+ * docs/pwg/building-testapp.xml:
+ Update the building-*.xml parts for 0.9 changes. All examples
+ code blocks compile in examples/pwg/*.
+
2005-06-30 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* docs/manual/advanced-autoplugging.xml:
diff --git a/configure.ac b/configure.ac
index 4a5fa89be..2891b8690 100644
--- a/configure.ac
+++ b/configure.ac
@@ -701,6 +701,7 @@ examples/manual/Makefile
examples/mixer/Makefile
examples/pingpong/Makefile
examples/plugins/Makefile
+examples/pwg/Makefile
examples/queue/Makefile
examples/queue2/Makefile
examples/queue3/Makefile
diff --git a/docs/pwg/advanced-interfaces.xml b/docs/pwg/advanced-interfaces.xml
index 7264dadf6..f99118227 100644
--- a/docs/pwg/advanced-interfaces.xml
+++ b/docs/pwg/advanced-interfaces.xml
@@ -118,6 +118,13 @@ gst_my_filter_some_interface_init (GstSomeInterface *iface)
</programlisting>
</sect1>
+ <sect1 id="section-iface-uri" xreflabel="URI interface">
+ <title>URI interface</title>
+ <para>
+ WRITEME
+ </para>
+ </sect1>
+
<sect1 id="section-iface-mixer" xreflabel="Mixer Interface">
<title>Mixer Interface</title>
<para>
diff --git a/docs/pwg/advanced-types.xml b/docs/pwg/advanced-types.xml
index 09710f39d..3f2f77c14 100644
--- a/docs/pwg/advanced-types.xml
+++ b/docs/pwg/advanced-types.xml
@@ -143,8 +143,8 @@ plugin_init (GstPlugin *plugin)
typefind functions.
</para>
<para>
- Autoplugging will be discussed in great detail in the chapter called
- <xref linkend="chapter-other-autoplugger"/>.
+ Autoplugging has been discussed in great detail in the Application
+ Development Manual.
</para>
</sect1>
diff --git a/docs/pwg/appendix-porting.xml b/docs/pwg/appendix-porting.xml
new file mode 100644
index 000000000..c0a1f1914
--- /dev/null
+++ b/docs/pwg/appendix-porting.xml
@@ -0,0 +1,37 @@
+<chapter id="chapter-porting">
+ <title>Porting 0.8 plug-ins to 0.9</title>
+ <para>
+ This section of the appendix will discuss shortly what changes to
+ plugins will be needed to quickly and conveniently port most
+ applications from &GStreamer;-0.8 to &GStreamer;-0.9, with references
+ to the relevant sections in this Plugin Writer's Guide where needed.
+ With this list, it should be possible to port most plugins to
+ &GStreamer;-0.9 in less than a day.
+ </para>
+
+ <sect1 id="section-porting-objects">
+ <title>List of changes</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Most functions returning an object or an object property have
+ been changed to return its own reference rather than a constant
+ reference of the one owned by the object itself. The reason for
+ this change is primarily threadsafety. This means, effectively,
+ that return values of functions such as
+ <function>gst_element_get_pad ()</function>,
+ <function>gst_pad_get_name ()</function> and many more like these
+ have to be free'ed or unreferenced after use. Check the API
+ references of each function to know for sure whether return
+ values should be free'ed or not.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ base classes, async capsnego (caps-on-buffer), async for sinks,
+ bytestream dead / pull_range, direct scheduling, etc.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect1>
+</chapter>
diff --git a/docs/pwg/building-boiler.xml b/docs/pwg/building-boiler.xml
index 8758c02f3..a4e74afcb 100644
--- a/docs/pwg/building-boiler.xml
+++ b/docs/pwg/building-boiler.xml
@@ -134,40 +134,54 @@ U gst-template/gst-app/src/Makefile.am
<example id="ex-boiler-examine-h">
<title>Example Plugin Header File</title>
- <programlisting>
- /* Definition of structure storing data for this element. */
- typedef struct _GstExample GstExample;
+ <programlisting><!-- example-begin filter.h a -->
+#include &lt;gst/gst.h&gt;
- struct _GstExample {
- GstElement element;
+/* Definition of structure storing data for this element. */
+typedef struct _GstMyFilter {
+ GstElement element;
- GstPad *sinkpad, *srcpad;
+ GstPad *sinkpad, *srcpad;
- gboolean silent;
- };
+ gboolean silent;
+<!-- example-end filter.h a -->
+<!-- example-begin filter.h b --><!--
+ gint samplerate, channels;
+--><!-- example-end filter.h b -->
+<!-- example-begin filter.h c -->
+} GstMyFilter;
- /* Standard definition defining a class for this element. */
- typedef struct _GstExampleClass GstExampleClass;
- struct _GstExampleClass {
- GstElementClass parent_class;
- };
+/* Standard definition defining a class for this element. */
+typedef struct _GstMyFilterClass {
+ GstElementClass parent_class;
+} GstMyFilterClass;
- /* Standard macros for defining types for this element. */
- #define GST_TYPE_EXAMPLE \
- (gst_example_get_type())
- #define GST_EXAMPLE(obj) \
- (G_TYPE_CHECK_CAST((obj),GST_TYPE_EXAMPLE,GstExample))
- #define GST_EXAMPLE_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_EXAMPLE,GstExample))
- #define GST_IS_EXAMPLE(obj) \
- (G_TYPE_CHECK_TYPE((obj),GST_TYPE_EXAMPLE))
- #define GST_IS_EXAMPLE_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_EXAMPLE))
+/* Standard macros for defining types for this element. */
+#define GST_TYPE_MY_FILTER \
+ (gst_my_filter_get_type())
+#define GST_MY_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MY_FILTER,GstMyFilter))
+#define GST_MY_FILTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MY_FILTER,GstMyFilterClass))
+#define GST_IS_MY_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MY_FILTER))
+#define GST_IS_MY_FILTER_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MY_FILTER))
- /* Standard function returning type information. */
- GType gst_example_get_type (void);
- </programlisting>
+/* Standard function returning type information. */
+GType gst_my_filter_get_type (void);
+ <!-- example-end filter.h c --></programlisting>
</example>
+ <para>
+ Using this header file, you can use the following macro to setup
+ the <classname>GObject</classname> basics in your source file so
+ that all functions will be called appropriately:
+ </para>
+ <programlisting><!-- example-begin boilerplate.c a -->
+#include "filter.h"
+
+GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
+ <!-- example-end boilerplate.c a --></programlisting>
</sect1>
<!-- ############ sect1 ############# -->
@@ -198,14 +212,14 @@ U gst-template/gst-app/src/Makefile.am
<para>
For example:
</para>
- <programlisting>
-static GstElementDetails example_details = {
+ <programlisting><!-- example-begin boilerplate.c b -->
+static GstElementDetails my_filter_details = {
"An example plugin",
"Example/FirstExample",
"Shows the basic structure of a plugin",
"your name &lt;your.name@your.isp&gt;"
};
- </programlisting>
+ <!-- example-end boilerplate.c b --></programlisting>
<para>
The element details are registered with the plugin during
the <function>_base_init ()</function> function, which is part of
@@ -213,17 +227,19 @@ static GstElementDetails example_details = {
should be set for this GObject in the function where you register
the type with Glib.
</para>
- <programlisting>
+ <programlisting><!-- example-begin boilerplate.c c -->
static void
-gst_my_filter_base_init (GstMyFilterClass *klass)
+gst_my_filter_base_init (gpointer klass)
{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+<!-- example-end boilerplate.c c -->
static GstElementDetails my_filter_details = {
[..]
};
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-[..]
+[..]<!-- example-begin boilerplate.c d -->
gst_element_class_set_details (element_class, &amp;my_filter_details);
+<!-- example-end boilerplate.c d -->
}
</programlisting>
</sect1>
@@ -258,7 +274,7 @@ gst_my_filter_base_init (GstMyFilterClass *klass)
<para>
For example:
</para>
- <programlisting>
+ <programlisting><!-- example-begin boilerplate.c e -->
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
"sink",
@@ -266,6 +282,16 @@ GST_STATIC_PAD_TEMPLATE (
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
+<!-- example-end boilerplate.c e -->
+<!-- example-begin boilerplate.c f --><!--
+static GstStaticPadTemplate src_factory =
+GST_STATIC_PAD_TEMPLATE (
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+);
+--><!-- example-end boilerplate.c f -->
</programlisting>
<para>
Those pad templates are registered during the
@@ -284,18 +310,30 @@ static GstStaticPadTemplate sink_factory = [..],
src_factory = [..];
static void
-gst_my_filter_base_init (GstMyFilterClass *klass)
+gst_my_filter_base_init (gpointer klass)
{
-[..]
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
+[..]
+<!-- example-begin boilerplate.c g -->
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&amp;src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&amp;sink_factory));
-[..]
}
- </programlisting>
+<!-- example-end boilerplate.c g -->
+<!-- example-begin boilerplate.c h --><!--
+static void
+gst_my_filter_class_init (GstMyFilterClass * klass)
+{
+}
+
+static void
+gst_my_filter_init (GstMyFilter * filter)
+{
+}
+
+#include "register.func"
+ --><!-- example-end boilerplate.c h --></programlisting>
<para>
The last argument in a template is its type
or list of supported types. In this example, we use 'ANY', which means
@@ -362,7 +400,7 @@ GST_STATIC_PAD_TEMPLATE (
Also, in this function, any supported element type in the plugin should
be registered.
</para>
- <programlisting>
+ <programlisting><!-- example-begin register.func -->
static gboolean
plugin_init (GstPlugin *plugin)
{
@@ -382,7 +420,7 @@ GST_PLUGIN_DEFINE (
"GStreamer",
"http://gstreamer.net/"
)
- </programlisting>
+ <!-- example-end register.func --></programlisting>
<para>
Note that the information returned by the plugin_init() function will be
cached in a central registry. For this reason, it is important that the
diff --git a/docs/pwg/building-chainfn.xml b/docs/pwg/building-chainfn.xml
index f9887628b..1cda9393f 100644
--- a/docs/pwg/building-chainfn.xml
+++ b/docs/pwg/building-chainfn.xml
@@ -10,61 +10,117 @@
one buffer will go out, too. Below is a very simple implementation of
a chain function:
</para>
- <programlisting>
-static void
-gst_my_filter_chain (GstPad *pad,
- GstData *data)
+ <programlisting><!-- example-begin chain.c a --><!--
+#include "init.func"
+#include "caps.func"
+static gboolean
+gst_my_filter_event (GstPad * pad, GstEvent * event)
{
- GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
- GstBuffer *buf = GST_BUFFER (data);
+ return gst_pad_event_default (pad, event);
+}
+--><!-- example-end chain.c a -->
+<!-- example-begin chain.c b -->
+static GstFlowReturn
+gst_my_filter_chain (GstPad *pad,
+ GstBuffer *buf)
+{
+ GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
if (!filter->silent)
g_print ("Have data of size %u bytes!\n", GST_BUFFER_SIZE (buf));
- gst_pad_push (filter->srcpad, GST_DATA (buf));
+ return gst_pad_push (filter->srcpad, buf);
}
- </programlisting>
+<!-- example-end chain.c b -->
+<!-- example-begin chain.c c --><!--
+static GstElementStateReturn
+gst_my_filter_change_state (GstElement * element)
+{
+ return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS,
+ change_state, (element), GST_STATE_SUCCESS);
+}
+#include "register.func"
+ --><!-- example-end chain.c c --></programlisting>
<para>
Obviously, the above doesn't do much useful. Instead of printing that the
data is in, you would normally process the data there. Remember, however,
that buffers are not always writable. In more advanced elements (the ones
- that do event processing), the incoming data might not even be a buffer.
+ that do event processing), you may want to additionally specify an event
+ handling function, which will be called when stream-events are sent (such
+ as end-of-stream, discontinuities, tags, etc.).
</para>
<programlisting>
static void
-gst_my_filter_chain (GstPad *pad,
- GstData *data)
+gst_my_filter_init (GstMyFilter * filter)
{
- GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
- GstBuffer *buf, *outbuf;
+[..]
+ gst_pad_set_event_function (filter-&gt;sinkpad,
+ gst_my_filter_event);
+[..]
+}
+<!-- example-begin chain2.c a --><!--
+#include "init.func"
+#include "caps.func"
+#include "chain.func"
+--><!-- example-end chain2.c a -->
+<!-- example-begin chain.func a --><!--
+static void
+gst_my_filter_stop_processing (GstMyFilter * filter)
+{
+}
- if (GST_IS_EVENT (data)) {
- GstEvent *event = GST_EVENT (data);
+static GstBuffer *
+gst_my_filter_process_data (GstMyFilter * filter, const GstBuffer * buf)
+{
+ return NULL;
+}
+--><!-- example-end chain.func a -->
+<!-- example-begin chain.func b -->
+static gboolean
+gst_my_filter_event (GstPad *pad,
+ GstEvent *event)
+{
+ GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- /* end-of-stream, we should close down all stream leftovers here */
- gst_my_filter_stop_processing (filter);
- /* fall-through to default event handling */
- default:
- gst_pad_event_default (pad, event);
- break;
- }
- return;
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ /* end-of-stream, we should close down all stream leftovers here */
+ gst_my_filter_stop_processing (filter);
+ break;
+ default:
+ break;
}
- buf = GST_BUFFER (data);
- outbuf = gst_my_filter_process_data (buf);
+ return gst_pad_event_default (pad, event);
+}
+
+static GstFlowReturn
+gst_my_filter_chain (GstPad *pad,
+ GstBuffer *buf)
+{
+ GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
+ GstBuffer *outbuf;
+
+ outbuf = gst_my_filter_process_data (filter, buf);
gst_buffer_unref (buf);
if (!outbuf) {
/* something went wrong - signal an error */
- gst_element_error (GST_ELEMENT (filter), STREAM, FAILED, (NULL), (NULL));
- return;
+ GST_ELEMENT_ERROR (GST_ELEMENT (filter), STREAM, FAILED, (NULL), (NULL));
+ return GST_FLOW_ERROR;
}
- gst_pad_push (filter->srcpad, GST_DATA (outbuf));
+ return gst_pad_push (filter->srcpad, outbuf);
+}
+<!-- example-end chain.func b -->
+<!-- example-begin chain2.c b --><!--
+static GstElementStateReturn
+gst_my_filter_change_state (GstElement * element)
+{
+ return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS,
+ change_state, (element), GST_STATE_SUCCESS);
}
- </programlisting>
+#include "register.func"
+ --><!-- example-end chain2.c b --></programlisting>
<para>
In some cases, it might be useful for an element to have control over the
input data rate, too. In that case, you probably want to write a so-called
diff --git a/docs/pwg/building-pads.xml b/docs/pwg/building-pads.xml
index d244ffdfc..f68008513 100644
--- a/docs/pwg/building-pads.xml
+++ b/docs/pwg/building-pads.xml
@@ -26,12 +26,65 @@
that, you have to register the pad with the element. This happens like
this:
</para>
- <programlisting>
-static GstPadLinkReturn gst_my_filter_link (GstPad *pad,
- const GstCaps *caps);
+ <programlisting><!-- example-begin init.func a --><!--
+#include "filter.h"
+#include &lt;string.h&gt;
+
+static GstElementStateReturn
+ gst_my_filter_change_state (GstElement * element);
+
+GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
+
+static void
+gst_my_filter_base_init (gpointer klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ static GstElementDetails my_filter_details = {
+ "An example plugin",
+ "Example/FirstExample",
+ "Shows the basic structure of a plugin",
+ "your name <your.name@your.isp>"
+ };
+ static GstStaticPadTemplate sink_factory =
+ GST_STATIC_PAD_TEMPLATE (
+ "sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+ static GstStaticPadTemplate src_factory =
+ GST_STATIC_PAD_TEMPLATE (
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+
+ gst_element_class_set_details (element_class, &my_filter_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_factory));
+}
+
+static void
+gst_my_filter_class_init (GstMyFilterClass * klass)
+{
+ GST_ELEMENT_CLASS (klass)->change_state = gst_my_filter_change_state;
+}
+--><!-- example-end init.func a -->
+<!-- example-begin init.func b -->
+static gboolean gst_my_filter_setcaps (GstPad *pad,
+ GstCaps *caps);
static GstCaps * gst_my_filter_getcaps (GstPad *pad);
-static void gst_my_filter_chain (GstPad *pad,
- GstData *data);
+static GstFlowReturn gst_my_filter_chain (GstPad *pad,
+ GstBuffer *buf);
+<!-- example-end init.func b -->
+<!-- example-begin init.func c --><!--
+static gboolean gst_my_filter_event (GstPad *pad,
+ GstEvent *event);
+--><!-- example-end init.func c -->
+<!-- example-begin init.func d -->
static void
gst_my_filter_init (GstMyFilter *filter)
@@ -41,20 +94,27 @@ gst_my_filter_init (GstMyFilter *filter)
/* pad through which data comes in to the element */
filter-&gt;sinkpad = gst_pad_new_from_template (
gst_element_class_get_pad_template (klass, "sink"), "sink");
- gst_pad_set_link_function (filter-&gt;sinkpad, gst_my_filter_link);
+ gst_pad_set_setcaps_function (filter-&gt;sinkpad, gst_my_filter_setcaps);
gst_pad_set_getcaps_function (filter-&gt;sinkpad, gst_my_filter_getcaps);
gst_pad_set_chain_function (filter-&gt;sinkpad, gst_my_filter_chain);
+<!-- example-end init.func d -->
+<!-- example-begin init.func e --><!--
+ gst_pad_set_event_function (filter-&gt;sinkpad, gst_my_filter_event);
+--><!-- example-end init.func e -->
+<!-- example-begin init.func f -->
gst_element_add_pad (GST_ELEMENT (filter), filter-&gt;sinkpad);
/* pad through which data goes out of the element */
filter-&gt;srcpad = gst_pad_new_from_template (
gst_element_class_get_pad_template (klass, "src"), "src");
- gst_pad_set_link_function (filter-&gt;srcpad, gst_my_filter_link);
+ gst_pad_set_setcaps_function (filter-&gt;srcpad, gst_my_filter_setcaps);
gst_pad_set_getcaps_function (filter-&gt;srcpad, gst_my_filter_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter-&gt;srcpad);
-[..]
+
+ /* properties initial value */
+ filter->silent = FALSE;
}
- </programlisting>
+ <!-- example-end init.func f --></programlisting>
<sect1 id="section-pads-linkfn" xreflabel="The link function">
<title>The link function</title>
@@ -72,10 +132,10 @@ gst_my_filter_init (GstMyFilter *filter)
If the element responds positively towards the streamtype, that type
will be used on the pad. An example:
</para>
- <programlisting>
-static GstPadLinkReturn
-gst_my_filter_link (GstPad *pad,
- const GstCaps *caps)
+ <programlisting><!-- example-begin caps.func a -->
+static gboolean
+gst_my_filter_setcaps (GstPad *pad,
+ GstCaps *caps)
{
GstStructure *structure = gst_caps_get_structure (caps, 0);
GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
@@ -97,7 +157,7 @@ gst_my_filter_link (GstPad *pad,
/* we're a filter and don't touch the properties of the data.
* That means we can set the given caps unmodified on the next
* element, and use that negotiation return value as ours. */
- ret = gst_pad_try_set_caps (otherpad, gst_caps_copy (caps));
+ ret = gst_pad_set_caps (otherpad, gst_caps_copy (caps));
if (GST_PAD_LINK_FAILED (ret))
return ret;
@@ -111,7 +171,7 @@ gst_my_filter_link (GstPad *pad,
return ret;
}
- </programlisting>
+ <!-- example-end caps.func a --></programlisting>
<para>
In here, we check the mimetype of the provided caps. Normally, you don't
need to do that in your own plugin/element, because the core does that
@@ -153,7 +213,7 @@ gst_my_filter_link (GstPad *pad,
specified on the other pad) on both pads. It explains how a
<function>_getcaps ()</function> can be used to do this.
</para>
- <programlisting>
+ <programlisting><!-- example-begin caps.func b -->
static GstCaps *
gst_my_filter_getcaps (GstPad *pad)
{
@@ -161,7 +221,7 @@ gst_my_filter_getcaps (GstPad *pad)
GstPad *otherpad = (pad == filter-&gt;srcpad) ? filter-&gt;sinkpad :
filter-&gt;srcpad;
GstCaps *othercaps = gst_pad_get_allowed_caps (otherpad), *caps;
- gint n;
+ gint i;
if (gst_caps_is_empty (othercaps))
return othercaps;
@@ -174,49 +234,36 @@ gst_my_filter_getcaps (GstPad *pad)
gst_structure_remove_field (structure, "rate");
}
caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
- gst_caps_free (othercaps);
+ gst_caps_unref (othercaps);
return caps;
}
- </programlisting>
+ <!-- example-end caps.func b --></programlisting>
</sect1>
+<!-- example-begin pads.c --><!--
+#include "init.func"
+#include "caps.func"
- <sect1 id="section-pads-explicitcaps" xreflabel="Explicit caps">
- <title>Explicit caps</title>
- <para>
- Obviously, many elements will not need the complex mechanism described in
- the previous sections, because they are much simpler than that.
- Such elements only support one format, or their format
- is fixed but the contents of the format depend on the stream or something
- else. In those cases, <emphasis>explicit caps</emphasis> are an easy way
- of handling caps. Explicit caps are an easy way of specifying one, fixed,
- supported format on a pad. Pads using explicit caps do not implement their
- own <function>_getcaps ()</function> or <function>_link ()</function>
- functions. When the exact format is known, an elements uses
- <function>gst_pad_set_explicit_caps ()</function> to specify the exact
- format. This is very useful for demuxers, for example.
- </para>
- <programlisting>
-static void
-gst_my_filter_init (GstMyFilter *filter)
+static gboolean
+gst_my_filter_event (GstPad * pad, GstEvent * event)
{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);
-[..]
- filter-&gt;srcpad = gst_pad_new_from_template (
- gst_element_class_get_pad_template (klass, "src"), "src");
- gst_pad_use_explicit_caps (filter-&gt;srcpad);
-[..]
+ return gst_pad_event_default (pad, event);
}
-static void
-gst_my_filter_somefunction (GstMyFilter *filter)
+static GstFlowReturn
+gst_my_filter_chain (GstPad * pad, GstBuffer * buf)
{
- GstCaps *caps = ..;
-[..]
- gst_pad_set_explicit_caps (filter-&gt;srcpad, caps);
-[..]
+ return gst_pad_push (GST_MY_FILTER (GST_OBJECT_PARENT (pad))->srcpad, buf);
}
- </programlisting>
- </sect1>
+
+static GstElementStateReturn
+gst_my_filter_change_state (GstElement * element)
+{
+ return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS,
+ change_state, (element), GST_STATE_SUCCESS);
+}
+
+#include "register.func"
+--><!-- example-end pads.c -->
</chapter>
diff --git a/docs/pwg/building-props.xml b/docs/pwg/building-props.xml
index 51f3def47..316140a1a 100644
--- a/docs/pwg/building-props.xml
+++ b/docs/pwg/building-props.xml
@@ -12,7 +12,19 @@
and can then fill in the value or take action required for that property
to change value internally.
</para>
- <programlisting>
+ <programlisting><!-- example-begin properties.c a --><!--
+#include "filter.h"
+GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
+static void
+gst_my_filter_base_init (gpointer klass)
+{
+}
+static void
+gst_my_filter_init (GstMyFilter * filter)
+{
+}
+--><!-- example-end properties.c a -->
+<!-- example-begin properties.c b -->
/* properties */
enum {
ARG_0,
@@ -82,7 +94,10 @@ gst_my_filter_get_property (GObject *object,
break;
}
}
- </programlisting>
+<!-- example-end properties.c b -->
+<!-- example-begin properties.c c --><!--
+#include "register.func"
+ --><!-- example-end properties.c c --></programlisting>
<para>
The above is a very simple example of how arguments are used. Graphical
applications - for example GStreamer Editor - will use these properties
diff --git a/docs/pwg/building-state.xml b/docs/pwg/building-state.xml
index 35286ff9e..d7ff8bbda 100644
--- a/docs/pwg/building-state.xml
+++ b/docs/pwg/building-state.xml
@@ -1,7 +1,5 @@
<chapter id="chapter-statemanage-states">
- <title>
- What are states?
- </title>
+ <title>What are states?</title>
<para>
A state describes whether the element instance is initialized, whether it
is ready to transfer data and whether it is currently handling data. There
@@ -55,9 +53,7 @@
</para>
<sect1 id="section-statemanage-filters">
- <title>
- Managing filter state
- </title>
+ <title>Managing filter state</title>
<para>
An element can be notified of state changes through a virtual function
pointer. Inside this function, the element can initialize any sort of
@@ -79,7 +75,24 @@ gst_my_filter_class_init (GstMyFilterClass *klass)
element_class->change_state = gst_my_filter_change_state;
}
-
+<!-- example-begin state.c a --><!--
+#include "init.func"
+#include "caps.func"
+#include "chain.func"
+#include "state.func"
+--><!-- example-end state.c a -->
+<!-- example-begin state.func a --><!--
+static gboolean
+gst_my_filter_allocate_memory (GstMyFilter * filter)
+{
+ return TRUE;
+}
+static void
+gst_my_filter_free_memory (GstMyFilter * filter)
+{
+}
+--><!-- example-end state.func a -->
+<!-- example-begin state.func b -->
static GstElementStateReturn
gst_my_filter_change_state (GstElement *element)
{
@@ -97,11 +110,12 @@ gst_my_filter_change_state (GstElement *element)
break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
-
- return GST_STATE_SUCCESS;
+ return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS,
+ change_state, (element), GST_STATE_SUCCESS);
}
- </programlisting>
+<!-- example-end state.func b -->
+<!-- example-begin state.c b --><!--
+#include "register.func"
+ --><!-- example-end state.c b --></programlisting>
</sect1>
</chapter>
diff --git a/docs/pwg/building-testapp.xml b/docs/pwg/building-testapp.xml
index 94135641e..7e2e9b195 100644
--- a/docs/pwg/building-testapp.xml
+++ b/docs/pwg/building-testapp.xml
@@ -63,25 +63,71 @@
the pipeline and make sure it doesn't crash.
</para>
- <programlisting>
+ <programlisting><!-- example-begin test.c -->
#include &lt;gst/gst.h&gt;
+static gboolean
+bus_call (GstBus *bus,
+ GstMessage *msg,
+ gpointer data)
+{
+ GMainLoop *loop = data;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:
+ g_print ("End-of-stream\n");
+ g_main_loop_quit (loop);
+ break;
+ case GST_MESSAGE_ERROR: {
+ gchar *debug;
+ GError *err;
+
+ gst_message_parse_error (msg, &amp;err, &amp;debug);
+ g_free (debug);
+
+ g_print ("Error: %s\n", err->message);
+ g_error_free (err);
+
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
gint
-main (gint arcg,
+main (gint argc,
gchar *argv[])
{
GstElement *pipeline, *filesrc, *decoder, *filter, *sink;
+ GMainLoop *loop;
/* initialization */
gst_init (&amp;argc, &amp;argv);
+ loop = g_main_loop_new (NULL, FALSE);
/* create elements */
pipeline = gst_pipeline_new ("my_pipeline");
+ gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (pipeline)),
+ bus_call, loop);
filesrc = gst_element_factory_make ("filesrc", "my_filesource");
decoder = gst_element_factory_make ("mad", "my_decoder");
filter = gst_element_factory_make ("my_filter", "my_filter");
sink = gst_element_factory_make ("osssink", "audiosink");
+ if (!sink || !decoder) {
+ g_print ("Decoder or output could not be found - check your install\n");
+ return -1;
+ } else if (!filter) {
+ g_print ("Your self-written filter could not be found. Make sure it "
+ "is installed correctly in $(libdir)/gstreamer-0.9/ and that "
+ "you've ran gst-register-0.9 to register it. Check availability "
+ "of the plugin afterwards using \"gst-inspect-0.9 my_filter\"");
+ return -1;
+ }
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
@@ -91,7 +137,7 @@ main (gint arcg,
/* run */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
- while (gst_bin_iterate (GST_BIN (pipeline)));
+ g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
@@ -99,5 +145,5 @@ main (gint arcg,
return 0;
}
- </programlisting>
+ <!-- example-end test.c --></programlisting>
</chapter>
diff --git a/docs/pwg/other-autoplugger.xml b/docs/pwg/other-autoplugger.xml
deleted file mode 100644
index ac4e4fd69..000000000
--- a/docs/pwg/other-autoplugger.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-<!-- ############ chapter ############# -->
-
-<chapter id="chapter-other-autoplugger" xreflabel="Writing an Autoplugger">
- <title>Writing an Autoplugger</title>
- <para>
- FIXME: write.
- </para>
-</chapter>
diff --git a/docs/pwg/pwg.xml b/docs/pwg/pwg.xml
index 46994f2fe..bcc87b0fe 100644
--- a/docs/pwg/pwg.xml
+++ b/docs/pwg/pwg.xml
@@ -36,10 +36,10 @@
<!ENTITY OTHER_ONETON SYSTEM "other-oneton.xml">
<!ENTITY OTHER_NTOONE SYSTEM "other-ntoone.xml">
<!ENTITY OTHER_NTON SYSTEM "other-nton.xml">
-<!ENTITY OTHER_AUTOPLUGGER SYSTEM "other-autoplugger.xml">
<!ENTITY OTHER_MANAGER SYSTEM "other-manager.xml">
<!ENTITY APPENDIX_CHECKLIST SYSTEM "appendix-checklist.xml">
+<!ENTITY APPENDIX_PORTING SYSTEM "appendix-porting.xml">
<!ENTITY APPENDIX_LICENSING SYSTEM "appendix-licensing.xml">
<!ENTITY APPENDIX_PYTHON SYSTEM "appendix-python.xml">
@@ -172,7 +172,6 @@
&OTHER_ONETON;
&OTHER_NTOONE;
&OTHER_NTON;
- &OTHER_AUTOPLUGGER;
&OTHER_MANAGER;
</part>
@@ -187,6 +186,7 @@
</partintro>
&APPENDIX_CHECKLIST;
+ &APPENDIX_PORTING;
&APPENDIX_LICENSING;
&APPENDIX_PYTHON;
</part>
diff --git a/examples/Makefile.am b/examples/Makefile.am
index ed48293d6..3646ccf2a 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -17,6 +17,7 @@ dirs = \
cutter \
pingpong \
manual \
+ pwg \
retag
SUBDIRS = $(dirs) \
diff --git a/examples/pwg/Makefile.am b/examples/pwg/Makefile.am
new file mode 100644
index 000000000..505e06de4
--- /dev/null
+++ b/examples/pwg/Makefile.am
@@ -0,0 +1,66 @@
+INCLUDES = $(GST_OBJ_CFLAGS) \
+ -DVERSION="\"0.0.1\""
+
+libboilerplate_la_SOURCES = boilerplate.c
+libpads_la_SOURCES = pads.c
+libchain_la_SOURCES = chain.c
+libchain2_la_SOURCES = chain2.c
+libstate_la_SOURCES = state.c
+libproperties_la_SOURCES = properties.c
+
+EXTRA_DIST = extract.pl
+
+EXAMPLES = \
+ libboilerplate.la \
+ libpads.la \
+ libchain.la \
+ libchain2.la \
+ libstate.la \
+ libproperties.la
+
+EXAMPLE_APPS = \
+ test
+
+filter.h register.func: $(top_srcdir)/docs/pwg/building-boiler.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-boiler.xml
+
+boilerplate.c: $(top_srcdir)/docs/pwg/building-boiler.xml filter.h register.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-boiler.xml
+
+caps.func init.func: $(top_srcdir)/docs/pwg/building-pads.xml filter.h
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-pads.xml
+
+pads.c: $(top_srcdir)/docs/pwg/building-pads.xml register.func caps.func init.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-pads.xml
+
+chain.func: $(top_srcdir)/docs/pwg/building-chainfn.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-chainfn.xml
+
+chain.c chain2.c: $(top_srcdir)/docs/pwg/building-chainfn.xml register.func caps.func init.func chain.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-chainfn.xml
+
+state.func: $(top_srcdir)/docs/pwg/building-state.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-state.xml
+
+state.c: $(top_srcdir)/docs/pwg/building-state.xml register.func caps.func init.func chain.func state.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-state.xml
+
+properties.c: $(top_srcdir)/docs/pwg/building-props.xml filter.h register.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-props.xml
+
+test.c: $(top_srcdir)/docs/pwg/building-testapp.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-testapp.xml
+
+noinst_PROGRAMS = $(EXAMPLE_APPS)
+noinst_LTLIBRARIES = $(EXAMPLES)
+LDADD = $(GST_OBJ_LIBS)
diff --git a/examples/pwg/extract.pl b/examples/pwg/extract.pl
new file mode 100755
index 000000000..5c257c70a
--- /dev/null
+++ b/examples/pwg/extract.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+# extract code fragments from xml program listings
+# first argument: source code file to find
+# second argument: xml files to extract code from
+
+# main
+
+# decodes xml by translating &amp; &lt; &gt; back to what they should be
+# and also ignore
+# <![CDATA[ and ]]> and <!-- and -->
+sub
+xml_decode ($)
+{
+ my $input = shift;
+
+ $input =~ s/\&amp;/&/g;
+ $input =~ s/&lt;/</g;
+ $input =~ s/&gt;/>/g;
+
+ if ($input =~ /<!\[CDATA\[/) { $input = ""; }
+ if ($input =~ /]]>/) { $input = ""; }
+ if ($input =~ /<!--/) { $input = ""; }
+ if ($input =~ /-->/) { $input = ""; }
+
+ #print "Returning line $input";
+ return $input;
+}
+
+# main
+my $output = shift @ARGV;
+
+$found = 0;
+%blocks = ();
+
+foreach $file (@ARGV)
+{
+ open FILE, $file or die "Cannot open file $file";
+
+ while ($line = <FILE>)
+ {
+ if ($line =~ /<!-- example-begin $output (.*?)-->/)
+ {
+ $found = 1;
+ $block_id = $1;
+ $block = "\n/*** block $block_id from $file ***/\n";
+
+ print "Extracting $output block $block_id from $file\n";
+
+ while ($line = <FILE>)
+ {
+ if ($line =~ /<!-- example-end $output (.*?)-->/)
+ {
+ last;
+ }
+ $block .= xml_decode ($line);
+ }
+ $blocks{$block_id} = $block;
+ }
+ }
+}
+
+
+if (!$found)
+{
+ print "Could not find $output example !\n";
+ exit(1);
+}
+
+# now output all the blocks in the right order
+open OUTPUT, ">$output";
+@block_ids = keys %blocks;
+foreach $block_id (sort @block_ids)
+{
+ print "Writing $output block $block_id\n";
+ print OUTPUT $blocks{$block_id};
+}
+close OUTPUT;
diff --git a/tests/old/examples/Makefile.am b/tests/old/examples/Makefile.am
index ed48293d6..3646ccf2a 100644
--- a/tests/old/examples/Makefile.am
+++ b/tests/old/examples/Makefile.am
@@ -17,6 +17,7 @@ dirs = \
cutter \
pingpong \
manual \
+ pwg \
retag
SUBDIRS = $(dirs) \
diff --git a/tests/old/examples/pwg/Makefile.am b/tests/old/examples/pwg/Makefile.am
new file mode 100644
index 000000000..505e06de4
--- /dev/null
+++ b/tests/old/examples/pwg/Makefile.am
@@ -0,0 +1,66 @@
+INCLUDES = $(GST_OBJ_CFLAGS) \
+ -DVERSION="\"0.0.1\""
+
+libboilerplate_la_SOURCES = boilerplate.c
+libpads_la_SOURCES = pads.c
+libchain_la_SOURCES = chain.c
+libchain2_la_SOURCES = chain2.c
+libstate_la_SOURCES = state.c
+libproperties_la_SOURCES = properties.c
+
+EXTRA_DIST = extract.pl
+
+EXAMPLES = \
+ libboilerplate.la \
+ libpads.la \
+ libchain.la \
+ libchain2.la \
+ libstate.la \
+ libproperties.la
+
+EXAMPLE_APPS = \
+ test
+
+filter.h register.func: $(top_srcdir)/docs/pwg/building-boiler.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-boiler.xml
+
+boilerplate.c: $(top_srcdir)/docs/pwg/building-boiler.xml filter.h register.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-boiler.xml
+
+caps.func init.func: $(top_srcdir)/docs/pwg/building-pads.xml filter.h
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-pads.xml
+
+pads.c: $(top_srcdir)/docs/pwg/building-pads.xml register.func caps.func init.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-pads.xml
+
+chain.func: $(top_srcdir)/docs/pwg/building-chainfn.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-chainfn.xml
+
+chain.c chain2.c: $(top_srcdir)/docs/pwg/building-chainfn.xml register.func caps.func init.func chain.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-chainfn.xml
+
+state.func: $(top_srcdir)/docs/pwg/building-state.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-state.xml
+
+state.c: $(top_srcdir)/docs/pwg/building-state.xml register.func caps.func init.func chain.func state.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-state.xml
+
+properties.c: $(top_srcdir)/docs/pwg/building-props.xml filter.h register.func
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-props.xml
+
+test.c: $(top_srcdir)/docs/pwg/building-testapp.xml
+ $(PERL_PATH) $(srcdir)/extract.pl $@ \
+ $(top_srcdir)/docs/pwg/building-testapp.xml
+
+noinst_PROGRAMS = $(EXAMPLE_APPS)
+noinst_LTLIBRARIES = $(EXAMPLES)
+LDADD = $(GST_OBJ_LIBS)
diff --git a/tests/old/examples/pwg/extract.pl b/tests/old/examples/pwg/extract.pl
new file mode 100755
index 000000000..5c257c70a
--- /dev/null
+++ b/tests/old/examples/pwg/extract.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+# extract code fragments from xml program listings
+# first argument: source code file to find
+# second argument: xml files to extract code from
+
+# main
+
+# decodes xml by translating &amp; &lt; &gt; back to what they should be
+# and also ignore
+# <![CDATA[ and ]]> and <!-- and -->
+sub
+xml_decode ($)
+{
+ my $input = shift;
+
+ $input =~ s/\&amp;/&/g;
+ $input =~ s/&lt;/</g;
+ $input =~ s/&gt;/>/g;
+
+ if ($input =~ /<!\[CDATA\[/) { $input = ""; }
+ if ($input =~ /]]>/) { $input = ""; }
+ if ($input =~ /<!--/) { $input = ""; }
+ if ($input =~ /-->/) { $input = ""; }
+
+ #print "Returning line $input";
+ return $input;
+}
+
+# main
+my $output = shift @ARGV;
+
+$found = 0;
+%blocks = ();
+
+foreach $file (@ARGV)
+{
+ open FILE, $file or die "Cannot open file $file";
+
+ while ($line = <FILE>)
+ {
+ if ($line =~ /<!-- example-begin $output (.*?)-->/)
+ {
+ $found = 1;
+ $block_id = $1;
+ $block = "\n/*** block $block_id from $file ***/\n";
+
+ print "Extracting $output block $block_id from $file\n";
+
+ while ($line = <FILE>)
+ {
+ if ($line =~ /<!-- example-end $output (.*?)-->/)
+ {
+ last;
+ }
+ $block .= xml_decode ($line);
+ }
+ $blocks{$block_id} = $block;
+ }
+ }
+}
+
+
+if (!$found)
+{
+ print "Could not find $output example !\n";
+ exit(1);
+}
+
+# now output all the blocks in the right order
+open OUTPUT, ">$output";
+@block_ids = keys %blocks;
+foreach $block_id (sort @block_ids)
+{
+ print "Writing $output block $block_id\n";
+ print OUTPUT $blocks{$block_id};
+}
+close OUTPUT;