| <chapter id="chapter-xml"> |
| <title>XML in <application>GStreamer</application></title> |
| <para> |
| <application>GStreamer</application> uses XML to store and load |
| its pipeline definitions. XML is also used internally to manage the |
| plugin registry. The plugin registry is a file that contains the definition |
| of all the plugins <application>GStreamer</application> knows about to have |
| quick access to the specifics of the plugins. |
| </para> |
| |
| <para> |
| We will show you how you can save a pipeline to XML and how you can reload that |
| XML file again for later use. |
| </para> |
| |
| <sect1 id="section-xml-write"> |
| <title>Turning GstElements into XML</title> |
| |
| <para> |
| We create a simple pipeline and write it to stdout with |
| gst_xml_write_file (). The following code constructs an MP3 player |
| pipeline with two threads and then writes out the XML both to stdout |
| and to a file. Use this program with one argument: the MP3 file on disk. |
| </para> |
| |
| <programlisting> |
| <!-- example-begin xml-mp3.c --> |
| #include <stdlib.h> |
| #include <gst/gst.h> |
| |
| gboolean playing; |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| GstElement *filesrc, *osssink, *queue, *queue2, *decode; |
| GstElement *bin; |
| GstElement *thread, *thread2; |
| |
| gst_init (&argc,&argv); |
| |
| if (argc != 2) { |
| g_print ("usage: %s <mp3 filename>\n", argv[0]); |
| exit (-1); |
| } |
| |
| /* create a new thread to hold the elements */ |
| thread = gst_element_factory_make ("thread", "thread"); |
| g_assert (thread != NULL); |
| thread2 = gst_element_factory_make ("thread", "thread2"); |
| g_assert (thread2 != NULL); |
| |
| /* create a new bin to hold the elements */ |
| bin = gst_bin_new ("bin"); |
| g_assert (bin != NULL); |
| |
| /* create a disk reader */ |
| filesrc = gst_element_factory_make ("filesrc", "disk_source"); |
| g_assert (filesrc != NULL); |
| g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); |
| |
| queue = gst_element_factory_make ("queue", "queue"); |
| queue2 = gst_element_factory_make ("queue", "queue2"); |
| |
| /* and an audio sink */ |
| osssink = gst_element_factory_make ("osssink", "play_audio"); |
| g_assert (osssink != NULL); |
| |
| decode = gst_element_factory_make ("mad", "decode"); |
| g_assert (decode != NULL); |
| |
| /* add objects to the main bin */ |
| gst_bin_add_many (GST_BIN (bin), filesrc, queue, NULL); |
| |
| gst_bin_add_many (GST_BIN (thread), decode, queue2, NULL); |
| |
| gst_bin_add (GST_BIN (thread2), osssink); |
| |
| gst_element_link_many (filesrc, queue, decode, queue2, osssink, NULL); |
| |
| gst_bin_add_many (GST_BIN (bin), thread, thread2, NULL); |
| |
| /* write the bin to stdout */ |
| gst_xml_write_file (GST_ELEMENT (bin), stdout); |
| |
| /* write the bin to a file */ |
| gst_xml_write_file (GST_ELEMENT (bin), fopen ("xmlTest.gst", "w")); |
| |
| exit (0); |
| } |
| <!-- example-end xml-mp3.c --> |
| </programlisting> |
| <para> |
| The most important line is: |
| </para> |
| <programlisting> |
| gst_xml_write_file (GST_ELEMENT (bin), stdout); |
| </programlisting> |
| <para> |
| gst_xml_write_file () will turn the given element into an xmlDocPtr that |
| is then formatted and saved to a file. To save to disk, pass the result |
| of a fopen(2) as the second argument. |
| </para> |
| <para> |
| The complete element hierarchy will be saved along with the inter element |
| pad links and the element parameters. Future <application>GStreamer</application> |
| versions will also allow you to store the signals in the XML file. |
| </para> |
| </sect1> |
| |
| <sect1 id="section-xml-load"> |
| <title>Loading a GstElement from an XML file</title> |
| <para> |
| Before an XML file can be loaded, you must create a GstXML object. |
| A saved XML file can then be loaded with the |
| gst_xml_parse_file (xml, filename, rootelement) method. |
| The root element can optionally left NULL. The following code example loads |
| the previously created XML file and runs it. |
| </para> |
| <programlisting> |
| #include <stdlib.h> |
| #include <gst/gst.h> |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| GstXML *xml; |
| GstElement *bin; |
| gboolean ret; |
| |
| gst_init (&argc, &argv); |
| |
| xml = gst_xml_new (); |
| |
| ret = gst_xml_parse_file(xml, "xmlTest.gst", NULL); |
| g_assert (ret == TRUE); |
| |
| bin = gst_xml_get_element (xml, "bin"); |
| g_assert (bin != NULL); |
| |
| gst_element_set_state (bin, GST_STATE_PLAYING); |
| |
| while (gst_bin_iterate(GST_BIN(bin))); |
| |
| gst_element_set_state (bin, GST_STATE_NULL); |
| |
| exit (0); |
| } |
| </programlisting> |
| <para> |
| gst_xml_get_element (xml, "name") can be used to get a specific element |
| from the XML file. |
| </para> |
| <para> |
| gst_xml_get_topelements (xml) can be used to get a list of all toplevel elements |
| in the XML file. |
| </para> |
| <para> |
| In addition to loading a file, you can also load a from a xmlDocPtr and |
| an in memory buffer using gst_xml_parse_doc and gst_xml_parse_memory |
| respectively. Both of these methods return a gboolean indicating |
| success or failure of the requested action. |
| </para> |
| </sect1> |
| <sect1 id="section-xml-custom"> |
| <title>Adding custom XML tags into the core XML data</title> |
| |
| <para> |
| It is possible to add custom XML tags to the core XML created with |
| gst_xml_write. This feature can be used by an application to add more |
| information to the save plugins. The editor will for example insert |
| the position of the elements on the screen using the custom XML tags. |
| </para> |
| <para> |
| It is strongly suggested to save and load the custom XML tags using |
| a namespace. This will solve the problem of having your XML tags |
| interfere with the core XML tags. |
| </para> |
| <para> |
| To insert a hook into the element saving procedure you can link |
| a signal to the GstElement using the following piece of code: |
| </para> |
| <programlisting> |
| xmlNsPtr ns; |
| |
| ... |
| ns = xmlNewNs (NULL, "http://gstreamer.net/gst-test/1.0/", "test"); |
| ... |
| thread = gst_element_factory_make ("thread", "thread"); |
| g_signal_connect (G_OBJECT (thread), "object_saved", |
| G_CALLBACK (object_saved), g_strdup ("decoder thread")); |
| ... |
| </programlisting> |
| <para> |
| When the thread is saved, the object_save method will be called. Our example |
| will insert a comment tag: |
| </para> |
| <programlisting> |
| static void |
| object_saved (GstObject *object, xmlNodePtr parent, gpointer data) |
| { |
| xmlNodePtr child; |
| |
| child = xmlNewChild (parent, ns, "comment", NULL); |
| xmlNewChild (child, ns, "text", (gchar *)data); |
| } |
| </programlisting> |
| <para> |
| Adding the custom tag code to the above example you will get an XML file |
| with the custom tags in it. Here's an excerpt: |
| </para> |
| <programlisting> |
| ... |
| <gst:element> |
| <gst:name>thread</gst:name> |
| <gst:type>thread</gst:type> |
| <gst:version>0.1.0</gst:version> |
| ... |
| </gst:children> |
| <test:comment> |
| <test:text>decoder thread</test:text> |
| </test:comment> |
| </gst:element> |
| ... |
| </programlisting> |
| <para> |
| To retrieve the custom XML again, you need to attach a signal to |
| the GstXML object used to load the XML data. You can then parse your |
| custom XML from the XML tree whenever an object is loaded. |
| </para> |
| |
| <para> |
| We can extend our previous example with the following piece of |
| code. |
| </para> |
| |
| <programlisting> |
| xml = gst_xml_new (); |
| |
| g_signal_connect (G_OBJECT (xml), "object_loaded", |
| G_CALLBACK (xml_loaded), xml); |
| |
| ret = gst_xml_parse_file (xml, "xmlTest.gst", NULL); |
| g_assert (ret == TRUE); |
| </programlisting> |
| |
| <para> |
| Whenever a new object has been loaded, the xml_loaded function will |
| be called. This function looks like: |
| </para> |
| <programlisting> |
| static void |
| xml_loaded (GstXML *xml, GstObject *object, xmlNodePtr self, gpointer data) |
| { |
| xmlNodePtr children = self->xmlChildrenNode; |
| |
| while (children) { |
| if (!strcmp (children->name, "comment")) { |
| xmlNodePtr nodes = children->xmlChildrenNode; |
| |
| while (nodes) { |
| if (!strcmp (nodes->name, "text")) { |
| gchar *name = g_strdup (xmlNodeGetContent (nodes)); |
| g_print ("object %s loaded with comment '%s'\n", |
| gst_object_get_name (object), name); |
| } |
| nodes = nodes->next; |
| } |
| } |
| children = children->next; |
| } |
| } |
| </programlisting> |
| <para> |
| As you can see, you'll get a handle to the GstXML object, the |
| newly loaded GstObject and the xmlNodePtr that was used to create |
| this object. In the above example we look for our special tag inside |
| the XML tree that was used to load the object and we print our |
| comment to the console. |
| </para> |
| </sect1> |
| |
| </chapter> |