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