[gst-devel] Namespaces and XML load/save (long)
Wim Taymans
wim.taymans at chello.be
Tue Jan 30 00:31:23 CET 2001
XML namespaces
==============
1) requirements
---------------
GStreamer uses XML to save a complete media pipeline. This
opens a lot of possibilities for end user apps, eg: save
constructed pipelines for later reuse, send pipelines over
the wire to render data elsewhere etc...
We will also allow the end user app to insert its own tags
into the GStreamer generated XML in order to give more
context to the elements. An example of such an app would be
gsteditor; while the GStreamer core will save the plain
element hierarchy, the editor will insert its own tags into
the XML tree to describe the elements positions, the
signals and other project data that might need to be
externalised.
And inherent problem with XML is that the use and naming of
the tags can be done in an arbitrary way. The GStreamer core
will, for example, use the 'element' and 'pad' tags to store
the element and pad objects. The description and the nesting
of the tags are described in a DTD (document type definition)
or soon with the proposed 'new' standard of the W3C; Schemas.
We can see obvious problems when a user app (unaware of the
core tags) is also trying to use the 'element' tag for its
own purposes. The core might try to load the 'element' tag
desscribing the user app data and the user app might get fed
with XML data of the GStreamer core.
In this document we try to describe how we will solve this problem
using namespaces.
2) namespaces
-------------
To avoid the tag conflicts we mentioned, the W3C has incorporated
XML namescapes.
Were a typical XML document without namespaces would look like:
<?xml version="1.0"?>
<GST-Pipeline>
<element>
<name>bin</name>
<children>
...
</children>
</element>
</GST-Pipeline>
Tha same document with namespaces look like this:
<?xml version="1.0"?>
<gst:GST-Pipeline xmlns:gst="http://gstreamer.net/gst-core/1.0/">
<gst:element>
<gst:name>bin</gst:name>
<gst:children>
...
</gst:children>
</gst:element>
</gst:GST-Pipeline>
In front of each tag we add, in this case, gst:. gst: is called the
namespace of the document and is declared with a statement:
<... xmlns:gst="http://gstreamer.net/gst-core/1.0/">
In this case we define a new namespace 'gst:' that is tied to the URL
http://gstreamer.net/gst-core/1.0/. This URL is typically based on
a domain you control and doesn't have to point to something valid on
the internet. Note that the following document is exactly the same
as the one mentioned above:
<?xml version="1.0"?>
<core:GST-Pipeline xmlns:core="http://gstreamer.net/gst-core/1.0/">
<core:element>
<core:name>bin</gst:name>
<core:children>
...
</core:children>
</core:element>
</core:GST-Pipeline>
GStreamer currently uses xmlns:gst="http://gstreamer.net/gst-core/1.0"
as
the namespace used for saving the core pipeline.
3) multi-namespace documents
----------------------------
Suppose we have a user app that wants to insert its own XML tags into
the core GStreamer XML pipelines, for the examples sake we will use the
editor as an example. The editor will insert XML tags inside each
element that describes the position and size of the element as it was
laid out in the editor.
For the examples sake, the tags that are used to descibe this meta data
will also be names 'element' in order to demonstrate the namespaces.
The editor will use its own namespace, being:
xmlns:editor="http://gstreamer.net/gst-editor/1.0". This namespace is
added to the XML documents header and all the elements the editor will
save will have this namespace attached to it. Our interleaved XML
document might look like this:
<?xml version="1.0"?>
<gst:GST-Pipeline xmlns:gst="http://gstreamer.net/gst-core/1.0/"
xmlns:editor="http://gstreamer.net/gst-editor/1.0/">
<gst:element>
<gst:name>bin</gst:name>
<gst:children>
...
</gst:children>
<editor:element>
<editor:position x="100" y="50"/>
</editor:element>
</gst:element>
</gst:GST-Pipeline>
As you can see, the namespaces clearly separate the same XML tags
'element'.
4) implementation considerations
--------------------------------
The GStreamer core doesn't know about user apps inserting data into the
XML, it does not look at XML tags not within its namespace when it
performs the parsing of the XML tree. The core, however, must be
able to hand over the XML tree to the user app so it can perform the
parsing of its tags inside its namespace. We therefore need hooks inside
the framework to accomplish this.
We also need hooks inside the GStreamer core to signal a user app that
it can now insert its tags into the XML tree.
4.1) XML save hooks
-------------------
GstObject has an abstract class method
xmlNodePtr (*save_thyself) (GstObject *object, xmlNodePtr parent)
A real element or pad will implement this function and construct an XML
representation of itself with the parent xmlNodePtr as the parent.
An element will typically call the save_thyself function of its
parent class before saving itself.
The XML save procedure on a GstObject is performed with:
gst_object_save_thyself (GstObject *object, xmlNodePtr parent)
Wich will call the elements implementation of the save_thyself function.
An app that wants to insert its XML tags has to connect to a signal of
the GstObject object called xml_saved. The object and the parent
xmlNodePtr will be passed to the signal handler of the user app, which
can then insert its tags.
The user app has no problem inserting its namespace into the xmlDoc and
neither will the GStreamer core.
4.2) XML load hooks
-------------------
The real problem lies in the loading of the XML tree. Before we load
the objects, we don't have anything to connect a signal to, so
another method has to be invented to signal the user app of the
freshly loaded objects.
One obvious solution would be to attach a class signal to the
GstObject class that would be fired whenever an object is created from
an XML document. Unfortunatly Gtk+ (or glib2.0) doesn't have class
signals so we need something else. Another problem with the class
signals would be that the user app would also be notified of object
creation outside its context. For example, if two threads perform an
XML load at the same time, the objects created in the first thread
would also notify the listener in the second thread. Both threads then
have to take care of not trying to parse XML documents that are from
the other thread, this obvously can get messy and we have to deal
with it without bothering the user app.
We'll continue with some random ramblings...
solution 1
----------
To solve this problem we can create a special method in gstobject
gst_object_loaded_notify_add (GstObjectLoadedCallback *callback,
xmlNodePtr doc)
this method would add the specified callback function to a list of
listeners and would perform the callback function if an object is
created (this can be done in the init method). The problem remains
though because we do not know the xmlNodePtr when we call the
callback. This seems messy.
sultion 2
---------
After the object has performed its restore_thyself, it is responsible
for signaling a object_loaded signal with the object and the xmlNodePtr
as an argument.
At object creation, the signal is connected to a singleton object
managed
by GstObject that can proxy the signal to the user app. This looks a
lot like a class signal. apps could also specify the xmlNodePtr they
are interested in and the signal would only be proxied to the app
if the xmlNodePtrs are from the same xmlDoc.
Solution 2 seems like a reasonable solution for now...
comments?
wtay
--
A pretty foot is one of the greatest gifts of nature... please send me your
last pair of shoes, already worn out in dancing... so I can have something
of yours to press against my heart.
-- Goethe
More information about the gstreamer-devel
mailing list