Add G_UNLIKELY in type registration.
[platform/upstream/gstreamer.git] / gst / gstxml.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstxml.c: XML save/restore of pipelines
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:gstxml
25  * @short_description: XML save/restore operations of pipelines
26  *
27  * GStreamer pipelines can be saved to xml files using gst_xml_write_file().
28  * They can be loaded back using gst_xml_parse_doc() / gst_xml_parse_file() / 
29  * gst_xml_parse_memory().
30  * Additionally one can load saved pipelines into the gst-editor to inspect the
31  * graph.
32  *
33  * #GstElement implementations need to override gst_object_save_thyself() and
34  * gst_object_restore_thyself().
35  */
36
37 #include "gst_private.h"
38
39 #include "gstxml.h"
40 #include "gstmarshal.h"
41 #include "gstinfo.h"
42 #include "gstbin.h"
43
44 enum
45 {
46   OBJECT_LOADED,
47   LAST_SIGNAL
48 };
49
50 static void gst_xml_class_init (GstXMLClass * klass);
51 static void gst_xml_init (GstXML * xml);
52
53 static void gst_xml_object_loaded (GstObject * private, GstObject * object,
54     xmlNodePtr self, gpointer data);
55
56 static GstObjectClass *parent_class = NULL;
57 static guint gst_xml_signals[LAST_SIGNAL] = { 0 };
58
59 GType
60 gst_xml_get_type (void)
61 {
62   static GType xml_type = 0;
63
64   if (G_UNLIKELY (xml_type == 0)) {
65     static const GTypeInfo xml_info = {
66       sizeof (GstXMLClass),
67       NULL,
68       NULL,
69       (GClassInitFunc) gst_xml_class_init,
70       NULL,
71       NULL,
72       sizeof (GstXML),
73       0,
74       (GInstanceInitFunc) gst_xml_init,
75       NULL
76     };
77
78     xml_type = g_type_register_static (GST_TYPE_OBJECT, "GstXML", &xml_info, 0);
79   }
80   return xml_type;
81 }
82
83 static void
84 gst_xml_class_init (GstXMLClass * klass)
85 {
86   GObjectClass *gobject_class;
87
88   gobject_class = (GObjectClass *) klass;
89
90   parent_class = g_type_class_peek_parent (klass);
91
92   /* FIXME G_TYPE_POINTER should be GType of xmlNodePtr
93    * (ensonic) can't be fixed, as libxml does not use GObject (unfortunately)
94    */
95   /**
96    * GstXML::object-loaded:
97    * @xml: the xml persistence instance
98    * @object: the object that has been loaded
99    * @xml_node: the related xml_node pointer to the document tree
100    *
101    * Signals that a new object has been deserialized.
102    */
103   gst_xml_signals[OBJECT_LOADED] =
104       g_signal_new ("object-loaded", G_TYPE_FROM_CLASS (klass),
105       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstXMLClass, object_loaded), NULL,
106       NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, GST_TYPE_OBJECT,
107       G_TYPE_POINTER);
108
109 }
110
111 static void
112 gst_xml_init (GstXML * xml)
113 {
114   xml->topelements = NULL;
115 }
116
117 /**
118  * gst_xml_new:
119  *
120  * Create a new GstXML parser object.
121  *
122  * Returns: a pointer to a new GstXML object.
123  */
124 GstXML *
125 gst_xml_new (void)
126 {
127   return GST_XML (g_object_new (GST_TYPE_XML, NULL));
128 }
129
130 /**
131  * gst_xml_write:
132  * @element: The element to write out
133  *
134  * Converts the given element into an XML presentation.
135  *
136  * Returns: a pointer to an XML document
137  */
138 xmlDocPtr
139 gst_xml_write (GstElement * element)
140 {
141   xmlDocPtr doc;
142   xmlNodePtr elementnode;
143   xmlNsPtr gst_ns;
144
145   doc = xmlNewDoc ((xmlChar *) "1.0");
146
147   doc->xmlRootNode = xmlNewDocNode (doc, NULL, (xmlChar *) "gstreamer", NULL);
148
149   gst_ns =
150       xmlNewNs (doc->xmlRootNode,
151       (xmlChar *) "http://gstreamer.net/gst-core/1.0/", (xmlChar *) "gst");
152
153   elementnode = xmlNewChild (doc->xmlRootNode, gst_ns, (xmlChar *) "element",
154       NULL);
155
156   gst_object_save_thyself (GST_OBJECT (element), elementnode);
157
158   return doc;
159 }
160
161 /**
162  * gst_xml_write_file:
163  * @element: The element to write out
164  * @out: an open file, like stdout
165  *
166  * Converts the given element into XML and writes the formatted XML to an open
167  * file.
168  *
169  * Returns: number of bytes written on success, -1 otherwise.
170  */
171 gint
172 gst_xml_write_file (GstElement * element, FILE * out)
173 {
174   xmlDocPtr cur;
175
176 #ifdef HAVE_LIBXML2
177   xmlOutputBufferPtr buf;
178 #endif
179   const char *encoding;
180   xmlCharEncodingHandlerPtr handler = NULL;
181   int indent;
182   gboolean ret;
183
184   cur = gst_xml_write (element);
185   if (!cur)
186     return -1;
187
188 #ifdef HAVE_LIBXML2
189   encoding = (const char *) cur->encoding;
190
191   if (encoding != NULL) {
192     xmlCharEncoding enc;
193
194     enc = xmlParseCharEncoding (encoding);
195
196     if (cur->charset != XML_CHAR_ENCODING_UTF8) {
197       xmlGenericError (xmlGenericErrorContext,
198           "xmlDocDump: document not in UTF8\n");
199       return -1;
200     }
201     if (enc != XML_CHAR_ENCODING_UTF8) {
202       handler = xmlFindCharEncodingHandler (encoding);
203       if (handler == NULL) {
204         xmlFree ((char *) cur->encoding);
205         cur->encoding = NULL;
206       }
207     }
208   }
209
210   buf = xmlOutputBufferCreateFile (out, handler);
211
212   indent = xmlIndentTreeOutput;
213   xmlIndentTreeOutput = 1;
214   ret = xmlSaveFormatFileTo (buf, cur, NULL, 1);
215   xmlIndentTreeOutput = indent;
216 #else
217   /* apparently this doesn't return anything in libxml1 */
218   xmlDocDump (out, cur);
219   ret = 1;
220 #endif
221
222   return ret;
223 }
224
225 /**
226  * gst_xml_parse_doc:
227  * @xml: a pointer to a GstXML object
228  * @doc: a pointer to an xml document to parse
229  * @root: The name of the root object to build
230  *
231  * Fills the GstXML object with the elements from the
232  * xmlDocPtr.
233  *
234  * Returns: TRUE on success, FALSE otherwise
235  */
236 gboolean
237 gst_xml_parse_doc (GstXML * xml, xmlDocPtr doc, const guchar * root)
238 {
239   xmlNodePtr field, cur;
240   xmlNsPtr ns;
241
242   cur = xmlDocGetRootElement (doc);
243   if (cur == NULL) {
244     g_warning ("gstxml: empty document\n");
245     return FALSE;
246   }
247   ns = xmlSearchNsByHref (doc, cur,
248       (xmlChar *) "http://gstreamer.net/gst-core/1.0/");
249   if (ns == NULL) {
250     g_warning ("gstxml: document of wrong type, core namespace not found\n");
251     return FALSE;
252   }
253   if (strcmp ((char *) cur->name, "gstreamer")) {
254     g_warning ("gstxml: XML file is in wrong format\n");
255     return FALSE;
256   }
257
258   gst_class_signal_connect (GST_OBJECT_CLASS (G_OBJECT_GET_CLASS (xml)),
259       "object_loaded", gst_xml_object_loaded, xml);
260
261   xml->ns = ns;
262
263   field = cur->xmlChildrenNode;
264
265   while (field) {
266     if (!strcmp ((char *) field->name, "element") && (field->ns == xml->ns)) {
267       GstElement *element;
268
269       element = gst_xml_make_element (field, NULL);
270
271       xml->topelements = g_list_prepend (xml->topelements, element);
272     }
273     field = field->next;
274   }
275
276   xml->topelements = g_list_reverse (xml->topelements);
277
278   return TRUE;
279 }
280
281 /* FIXME 0.9: Why guchar*? */
282 /**
283  * gst_xml_parse_file:
284  * @xml: a pointer to a GstXML object
285  * @fname: The filename with the xml description
286  * @root: The name of the root object to build
287  *
288  * Fills the GstXML object with the corresponding elements from
289  * the XML file fname. Optionally it will only build the element from
290  * the element node root (if it is not NULL). This feature is useful
291  * if you only want to build a specific element from an XML file
292  * but not the pipeline it is embedded in.
293  *
294  * Pass "-" as fname to read from stdin. You can also pass a URI
295  * of any format that libxml supports, including http.
296  *
297  * Returns: TRUE on success, FALSE otherwise
298  */
299 gboolean
300 gst_xml_parse_file (GstXML * xml, const guchar * fname, const guchar * root)
301 {
302   xmlDocPtr doc;
303
304   g_return_val_if_fail (fname != NULL, FALSE);
305
306   doc = xmlParseFile ((char *) fname);
307
308   if (!doc) {
309     g_warning ("gstxml: XML file \"%s\" could not be read\n", fname);
310     return FALSE;
311   }
312
313   return gst_xml_parse_doc (xml, doc, root);
314 }
315
316 /* FIXME 0.9: guchar* */
317 /**
318  * gst_xml_parse_memory:
319  * @xml: a pointer to a GstXML object
320  * @buffer: a pointer to the in memory XML buffer
321  * @size: the size of the buffer
322  * @root: the name of the root objects to build
323  *
324  * Fills the GstXML object with the corresponding elements from
325  * an in memory XML buffer.
326  *
327  * Returns: TRUE on success
328  */
329 gboolean
330 gst_xml_parse_memory (GstXML * xml, guchar * buffer, guint size,
331     const gchar * root)
332 {
333   xmlDocPtr doc;
334
335   g_return_val_if_fail (buffer != NULL, FALSE);
336
337   doc = xmlParseMemory ((char *) buffer, size);
338
339   return gst_xml_parse_doc (xml, doc, (const xmlChar *) root);
340 }
341
342 static void
343 gst_xml_object_loaded (GstObject * private, GstObject * object, xmlNodePtr self,
344     gpointer data)
345 {
346   GstXML *xml = GST_XML (data);
347
348   /* FIXME check that this element was created from the same xmlDocPtr... */
349   g_signal_emit (G_OBJECT (xml), gst_xml_signals[OBJECT_LOADED], 0, object,
350       self);
351 }
352
353 /**
354  * gst_xml_get_topelements:
355  * @xml: The GstXML to get the elements from
356  *
357  * Retrive a list of toplevel elements.
358  *
359  * Returns: a GList of elements
360  */
361 GList *
362 gst_xml_get_topelements (GstXML * xml)
363 {
364   g_return_val_if_fail (xml != NULL, NULL);
365
366   return xml->topelements;
367 }
368
369 /* FIXME 0.9: why is the arg guchar* instead of gchar*? */
370 /**
371  * gst_xml_get_element:
372  * @xml: The GstXML to get the element from
373  * @name: The name of element to retreive
374  *
375  * This function is used to get a pointer to the GstElement corresponding
376  * to name in the pipeline description. You would use this if you have
377  * to do anything to the element after loading.
378  *
379  * Returns: a pointer to a new GstElement
380  */
381 GstElement *
382 gst_xml_get_element (GstXML * xml, const guchar * name)
383 {
384   GstElement *element;
385   GList *topelements;
386
387   g_return_val_if_fail (xml != NULL, NULL);
388   g_return_val_if_fail (name != NULL, NULL);
389
390   GST_DEBUG ("gstxml: getting element \"%s\"", name);
391
392   topelements = gst_xml_get_topelements (xml);
393
394   while (topelements) {
395     GstElement *top = GST_ELEMENT (topelements->data);
396
397     GST_DEBUG ("gstxml: getting element \"%s\"", name);
398     if (!strcmp (GST_ELEMENT_NAME (top), (char *) name)) {
399       return top;
400     } else {
401       if (GST_IS_BIN (top)) {
402         element = gst_bin_get_by_name (GST_BIN (top), (gchar *) name);
403
404         if (element)
405           return element;
406       }
407     }
408     topelements = g_list_next (topelements);
409   }
410   return NULL;
411 }
412
413 /**
414  * gst_xml_make_element:
415  * @cur: the xml node
416  * @parent: the parent of this object when it's loaded
417  *
418  * Load the element from the XML description
419  *
420  * Returns: the new element
421  */
422 GstElement *
423 gst_xml_make_element (xmlNodePtr cur, GstObject * parent)
424 {
425   xmlNodePtr children = cur->xmlChildrenNode;
426   GstElement *element;
427   gchar *name = NULL;
428   gchar *type = NULL;
429
430   /* first get the needed tags to construct the element */
431   while (children) {
432     if (!strcmp ((char *) children->name, "name")) {
433       name = (gchar *) xmlNodeGetContent (children);
434     } else if (!strcmp ((char *) children->name, "type")) {
435       type = (gchar *) xmlNodeGetContent (children);
436     }
437     children = children->next;
438   }
439   g_return_val_if_fail (name != NULL, NULL);
440   g_return_val_if_fail (type != NULL, NULL);
441
442   GST_CAT_INFO (GST_CAT_XML, "loading \"%s\" of type \"%s\"", name, type);
443
444   element = gst_element_factory_make (type, name);
445
446   g_return_val_if_fail (element != NULL, NULL);
447
448   g_free (type);
449   g_free (name);
450
451   /* ne need to set the parent on this object bacause the pads */
452   /* will go through the hierarchy to link to their peers */
453   if (parent)
454     gst_object_set_parent (GST_OBJECT (element), parent);
455
456   gst_object_restore_thyself (GST_OBJECT (element), cur);
457
458   return element;
459 }