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 */
23 #include "gst_private.h"
25 #include "gstxml.h"
26 #include "gstmarshal.h"
27 #include "gstinfo.h"
28 #include "gstbin.h"
30 enum
31 {
32 OBJECT_LOADED,
33 LAST_SIGNAL
34 };
36 static void gst_xml_class_init (GstXMLClass * klass);
37 static void gst_xml_init (GstXML * xml);
39 static void gst_xml_object_loaded (GstObject * private, GstObject * object,
40 xmlNodePtr self, gpointer data);
42 static GstObjectClass *parent_class = NULL;
43 static guint gst_xml_signals[LAST_SIGNAL] = { 0 };
45 GType
46 gst_xml_get_type (void)
47 {
48 static GType xml_type = 0;
50 if (!xml_type) {
51 static const GTypeInfo xml_info = {
52 sizeof (GstXMLClass),
53 NULL,
54 NULL,
55 (GClassInitFunc) gst_xml_class_init,
56 NULL,
57 NULL,
58 sizeof (GstXML),
59 0,
60 (GInstanceInitFunc) gst_xml_init,
61 NULL
62 };
64 xml_type = g_type_register_static (GST_TYPE_OBJECT, "GstXML", &xml_info, 0);
65 }
66 return xml_type;
67 }
69 static void
70 gst_xml_class_init (GstXMLClass * klass)
71 {
72 GObjectClass *gobject_class;
74 gobject_class = (GObjectClass *) klass;
76 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
78 /* FIXME G_TYPE_POINTER should be GType of xmlNodePtr */
79 gst_xml_signals[OBJECT_LOADED] =
80 g_signal_new ("object-loaded", G_TYPE_FROM_CLASS (klass),
81 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstXMLClass, object_loaded), NULL,
82 NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, GST_TYPE_OBJECT,
83 G_TYPE_POINTER);
85 }
87 static void
88 gst_xml_init (GstXML * xml)
89 {
90 xml->topelements = NULL;
91 }
93 /**
94 * gst_xml_new:
95 *
96 * Create a new GstXML parser object.
97 *
98 * Returns: a pointer to a new GstXML object.
99 */
100 GstXML *
101 gst_xml_new (void)
102 {
103 return GST_XML (g_object_new (GST_TYPE_XML, NULL));
104 }
106 /**
107 * gst_xml_write:
108 * @element: The element to write out
109 *
110 * Converts the given element into an XML presentation.
111 *
112 * Returns: a pointer to an XML document
113 */
114 xmlDocPtr
115 gst_xml_write (GstElement * element)
116 {
117 xmlDocPtr doc;
118 xmlNodePtr elementnode;
119 xmlNsPtr gst_ns;
121 doc = xmlNewDoc ("1.0");
123 doc->xmlRootNode = xmlNewDocNode (doc, NULL, "gstreamer", NULL);
125 gst_ns =
126 xmlNewNs (doc->xmlRootNode, "http://gstreamer.net/gst-core/1.0/", "gst");
128 elementnode = xmlNewChild (doc->xmlRootNode, gst_ns, "element", NULL);
130 gst_object_save_thyself (GST_OBJECT (element), elementnode);
132 return doc;
133 }
135 /**
136 * gst_xml_write_file:
137 * @element: The element to write out
138 * @out: an open file, like stdout
139 *
140 * Converts the given element into XML and writes the formatted XML to an open
141 * file.
142 *
143 * Returns: number of bytes written on success, -1 otherwise.
144 */
145 gint
146 gst_xml_write_file (GstElement * element, FILE * out)
147 {
148 xmlDocPtr cur;
150 #ifdef HAVE_LIBXML2
151 xmlOutputBufferPtr buf;
152 #endif
153 const char *encoding;
154 xmlCharEncodingHandlerPtr handler = NULL;
155 int indent;
156 gboolean ret;
158 cur = gst_xml_write (element);
159 if (!cur)
160 return -1;
162 #ifdef HAVE_LIBXML2
163 encoding = (const char *) cur->encoding;
165 if (encoding != NULL) {
166 xmlCharEncoding enc;
168 enc = xmlParseCharEncoding (encoding);
170 if (cur->charset != XML_CHAR_ENCODING_UTF8) {
171 xmlGenericError (xmlGenericErrorContext,
172 "xmlDocDump: document not in UTF8\n");
173 return -1;
174 }
175 if (enc != XML_CHAR_ENCODING_UTF8) {
176 handler = xmlFindCharEncodingHandler (encoding);
177 if (handler == NULL) {
178 xmlFree ((char *) cur->encoding);
179 cur->encoding = NULL;
180 }
181 }
182 }
184 buf = xmlOutputBufferCreateFile (out, handler);
186 indent = xmlIndentTreeOutput;
187 xmlIndentTreeOutput = 1;
188 ret = xmlSaveFormatFileTo (buf, cur, NULL, 1);
189 xmlIndentTreeOutput = indent;
190 #else
191 /* apparently this doesn't return anything in libxml1 */
192 xmlDocDump (out, cur);
193 ret = 1;
194 #endif
196 return ret;
197 }
199 /**
200 * gst_xml_parse_doc:
201 * @xml: a pointer to a GstXML object
202 * @doc: a pointer to an xml document to parse
203 * @root: The name of the root object to build
204 *
205 * Fills the GstXML object with the elements from the
206 * xmlDocPtr.
207 *
208 * Returns: TRUE on success, FALSE otherwise
209 */
210 gboolean
211 gst_xml_parse_doc (GstXML * xml, xmlDocPtr doc, const guchar * root)
212 {
213 xmlNodePtr field, cur;
214 xmlNsPtr ns;
216 cur = xmlDocGetRootElement (doc);
217 if (cur == NULL) {
218 g_warning ("gstxml: empty document\n");
219 return FALSE;
220 }
221 ns = xmlSearchNsByHref (doc, cur, "http://gstreamer.net/gst-core/1.0/");
222 if (ns == NULL) {
223 g_warning ("gstxml: document of wrong type, core namespace not found\n");
224 return FALSE;
225 }
226 if (strcmp (cur->name, "gstreamer")) {
227 g_warning ("gstxml: XML file is in wrong format\n");
228 return FALSE;
229 }
231 gst_class_signal_connect (GST_OBJECT_CLASS (G_OBJECT_GET_CLASS (xml)),
232 "object_loaded", gst_xml_object_loaded, xml);
234 xml->ns = ns;
236 field = cur->xmlChildrenNode;
238 while (field) {
239 if (!strcmp (field->name, "element") && (field->ns == xml->ns)) {
240 GstElement *element;
242 element = gst_xml_make_element (field, NULL);
244 xml->topelements = g_list_prepend (xml->topelements, element);
245 }
246 field = field->next;
247 }
249 xml->topelements = g_list_reverse (xml->topelements);
251 return TRUE;
252 }
254 /**
255 * gst_xml_parse_file:
256 * @xml: a pointer to a GstXML object
257 * @fname: The filename with the xml description
258 * @root: The name of the root object to build
259 *
260 * Fills the GstXML object with the corresponding elements from
261 * the XML file fname. Optionally it will only build the element from
262 * the element node root (if it is not NULL). This feature is useful
263 * if you only want to build a specific element from an XML file
264 * but not the pipeline it is embedded in.
265 *
266 * Pass "-" as fname to read from stdin. You can also pass a URI
267 * of any format that libxml supports, including http.
268 *
269 * Returns: TRUE on success, FALSE otherwise
270 */
271 gboolean
272 gst_xml_parse_file (GstXML * xml, const guchar * fname, const guchar * root)
273 {
274 xmlDocPtr doc;
276 g_return_val_if_fail (fname != NULL, FALSE);
278 doc = xmlParseFile (fname);
280 if (!doc) {
281 g_warning ("gstxml: XML file \"%s\" could not be read\n", fname);
282 return FALSE;
283 }
285 return gst_xml_parse_doc (xml, doc, root);
286 }
288 /**
289 * gst_xml_parse_memory:
290 * @xml: a pointer to a GstXML object
291 * @buffer: a pointer to the in memory XML buffer
292 * @size: the size of the buffer
293 * @root: the name of the root objects to build
294 *
295 * Fills the GstXML object with the corresponding elements from
296 * an in memory XML buffer.
297 *
298 * Returns: TRUE on success
299 */
300 gboolean
301 gst_xml_parse_memory (GstXML * xml, guchar * buffer, guint size,
302 const gchar * root)
303 {
304 xmlDocPtr doc;
306 g_return_val_if_fail (buffer != NULL, FALSE);
308 doc = xmlParseMemory (buffer, size);
310 return gst_xml_parse_doc (xml, doc, root);
311 }
313 static void
314 gst_xml_object_loaded (GstObject * private, GstObject * object, xmlNodePtr self,
315 gpointer data)
316 {
317 GstXML *xml = GST_XML (data);
319 /* FIXME check that this element was created from the same xmlDocPtr... */
320 g_signal_emit (G_OBJECT (xml), gst_xml_signals[OBJECT_LOADED], 0, object,
321 self);
322 }
324 /**
325 * gst_xml_get_topelements:
326 * @xml: The GstXML to get the elements from
327 *
328 * Retrive a list of toplevel elements.
329 *
330 * Returns: a GList of elements
331 */
332 GList *
333 gst_xml_get_topelements (GstXML * xml)
334 {
335 g_return_val_if_fail (xml != NULL, NULL);
337 return xml->topelements;
338 }
340 /**
341 * gst_xml_get_element:
342 * @xml: The GstXML to get the element from
343 * @name: The name of element to retreive
344 *
345 * This function is used to get a pointer to the GstElement corresponding
346 * to name in the pipeline description. You would use this if you have
347 * to do anything to the element after loading.
348 *
349 * Returns: a pointer to a new GstElement
350 */
351 GstElement *
352 gst_xml_get_element (GstXML * xml, const guchar * name)
353 {
354 GstElement *element;
355 GList *topelements;
357 g_return_val_if_fail (xml != NULL, NULL);
358 g_return_val_if_fail (name != NULL, NULL);
360 GST_DEBUG ("gstxml: getting element \"%s\"", name);
362 topelements = gst_xml_get_topelements (xml);
364 while (topelements) {
365 GstElement *top = GST_ELEMENT (topelements->data);
367 GST_DEBUG ("gstxml: getting element \"%s\"", name);
368 if (!strcmp (GST_ELEMENT_NAME (top), name)) {
369 return top;
370 } else {
371 if (GST_IS_BIN (top)) {
372 element = gst_bin_get_by_name (GST_BIN (top), name);
374 if (element)
375 return element;
376 }
377 }
378 topelements = g_list_next (topelements);
379 }
380 return NULL;
381 }
383 /**
384 * gst_xml_make_element:
385 * @cur: the xml node
386 * @parent: the parent of this object when it's loaded
387 *
388 * Load the element from the XML description
389 *
390 * Returns: the new element
391 */
392 GstElement *
393 gst_xml_make_element (xmlNodePtr cur, GstObject * parent)
394 {
395 xmlNodePtr children = cur->xmlChildrenNode;
396 GstElement *element;
397 guchar *name = NULL;
398 guchar *type = NULL;
400 /* first get the needed tags to construct the element */
401 while (children) {
402 if (!strcmp (children->name, "name")) {
403 name = xmlNodeGetContent (children);
404 } else if (!strcmp (children->name, "type")) {
405 type = xmlNodeGetContent (children);
406 }
407 children = children->next;
408 }
409 g_return_val_if_fail (name != NULL, NULL);
410 g_return_val_if_fail (type != NULL, NULL);
412 GST_CAT_INFO (GST_CAT_XML, "loading \"%s\" of type \"%s\"", name, type);
414 element = gst_element_factory_make (type, name);
416 g_return_val_if_fail (element != NULL, NULL);
418 /* ne need to set the parent on this object bacause the pads */
419 /* will go through the hierarchy to link to their peers */
420 if (parent)
421 gst_object_set_parent (GST_OBJECT (element), parent);
423 gst_object_restore_thyself (GST_OBJECT (element), cur);
425 return element;
426 }