[21447] | 1 | <chapter id="chapter-xml"> |
---|
| 2 | <title>XML in <application>GStreamer</application></title> |
---|
| 3 | <para> |
---|
| 4 | <application>GStreamer</application> uses XML to store and load |
---|
| 5 | its pipeline definitions. XML is also used internally to manage the |
---|
| 6 | plugin registry. The plugin registry is a file that contains the definition |
---|
| 7 | of all the plugins <application>GStreamer</application> knows about to have |
---|
| 8 | quick access to the specifics of the plugins. |
---|
| 9 | </para> |
---|
| 10 | |
---|
| 11 | <para> |
---|
| 12 | We will show you how you can save a pipeline to XML and how you can reload that |
---|
| 13 | XML file again for later use. |
---|
| 14 | </para> |
---|
| 15 | |
---|
| 16 | <sect1 id="section-xml-write"> |
---|
| 17 | <title>Turning GstElements into XML</title> |
---|
| 18 | |
---|
| 19 | <para> |
---|
| 20 | We create a simple pipeline and write it to stdout with |
---|
| 21 | gst_xml_write_file (). The following code constructs an MP3 player |
---|
| 22 | pipeline with two threads and then writes out the XML both to stdout |
---|
| 23 | and to a file. Use this program with one argument: the MP3 file on disk. |
---|
| 24 | </para> |
---|
| 25 | |
---|
| 26 | <programlisting> |
---|
| 27 | <!-- example-begin xml-mp3.c --> |
---|
| 28 | #include <stdlib.h> |
---|
| 29 | #include <gst/gst.h> |
---|
| 30 | |
---|
| 31 | gboolean playing; |
---|
| 32 | |
---|
| 33 | int |
---|
| 34 | main (int argc, char *argv[]) |
---|
| 35 | { |
---|
| 36 | GstElement *filesrc, *osssink, *queue, *queue2, *decode; |
---|
| 37 | GstElement *bin; |
---|
| 38 | GstElement *thread, *thread2; |
---|
| 39 | |
---|
| 40 | gst_init (&argc,&argv); |
---|
| 41 | |
---|
| 42 | if (argc != 2) { |
---|
| 43 | g_print ("usage: %s <mp3 filename>\n", argv[0]); |
---|
| 44 | exit (-1); |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | /* create a new thread to hold the elements */ |
---|
| 48 | thread = gst_element_factory_make ("thread", "thread"); |
---|
| 49 | g_assert (thread != NULL); |
---|
| 50 | thread2 = gst_element_factory_make ("thread", "thread2"); |
---|
| 51 | g_assert (thread2 != NULL); |
---|
| 52 | |
---|
| 53 | /* create a new bin to hold the elements */ |
---|
| 54 | bin = gst_bin_new ("bin"); |
---|
| 55 | g_assert (bin != NULL); |
---|
| 56 | |
---|
| 57 | /* create a disk reader */ |
---|
| 58 | filesrc = gst_element_factory_make ("filesrc", "disk_source"); |
---|
| 59 | g_assert (filesrc != NULL); |
---|
| 60 | g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); |
---|
| 61 | |
---|
| 62 | queue = gst_element_factory_make ("queue", "queue"); |
---|
| 63 | queue2 = gst_element_factory_make ("queue", "queue2"); |
---|
| 64 | |
---|
| 65 | /* and an audio sink */ |
---|
| 66 | osssink = gst_element_factory_make ("osssink", "play_audio"); |
---|
| 67 | g_assert (osssink != NULL); |
---|
| 68 | |
---|
| 69 | decode = gst_element_factory_make ("mad", "decode"); |
---|
| 70 | g_assert (decode != NULL); |
---|
| 71 | |
---|
| 72 | /* add objects to the main bin */ |
---|
| 73 | gst_bin_add_many (GST_BIN (bin), filesrc, queue, NULL); |
---|
| 74 | |
---|
| 75 | gst_bin_add_many (GST_BIN (thread), decode, queue2, NULL); |
---|
| 76 | |
---|
| 77 | gst_bin_add (GST_BIN (thread2), osssink); |
---|
| 78 | |
---|
| 79 | gst_element_link_many (filesrc, queue, decode, queue2, osssink, NULL); |
---|
| 80 | |
---|
| 81 | gst_bin_add_many (GST_BIN (bin), thread, thread2, NULL); |
---|
| 82 | |
---|
| 83 | /* write the bin to stdout */ |
---|
| 84 | gst_xml_write_file (GST_ELEMENT (bin), stdout); |
---|
| 85 | |
---|
| 86 | /* write the bin to a file */ |
---|
| 87 | gst_xml_write_file (GST_ELEMENT (bin), fopen ("xmlTest.gst", "w")); |
---|
| 88 | |
---|
| 89 | exit (0); |
---|
| 90 | } |
---|
| 91 | <!-- example-end xml-mp3.c --> |
---|
| 92 | </programlisting> |
---|
| 93 | <para> |
---|
| 94 | The most important line is: |
---|
| 95 | </para> |
---|
| 96 | <programlisting> |
---|
| 97 | gst_xml_write_file (GST_ELEMENT (bin), stdout); |
---|
| 98 | </programlisting> |
---|
| 99 | <para> |
---|
| 100 | gst_xml_write_file () will turn the given element into an xmlDocPtr that |
---|
| 101 | is then formatted and saved to a file. To save to disk, pass the result |
---|
| 102 | of a fopen(2) as the second argument. |
---|
| 103 | </para> |
---|
| 104 | <para> |
---|
| 105 | The complete element hierarchy will be saved along with the inter element |
---|
| 106 | pad links and the element parameters. Future <application>GStreamer</application> |
---|
| 107 | versions will also allow you to store the signals in the XML file. |
---|
| 108 | </para> |
---|
| 109 | </sect1> |
---|
| 110 | |
---|
| 111 | <sect1 id="section-xml-load"> |
---|
| 112 | <title>Loading a GstElement from an XML file</title> |
---|
| 113 | <para> |
---|
| 114 | Before an XML file can be loaded, you must create a GstXML object. |
---|
| 115 | A saved XML file can then be loaded with the |
---|
| 116 | gst_xml_parse_file (xml, filename, rootelement) method. |
---|
| 117 | The root element can optionally left NULL. The following code example loads |
---|
| 118 | the previously created XML file and runs it. |
---|
| 119 | </para> |
---|
| 120 | <programlisting> |
---|
| 121 | #include <stdlib.h> |
---|
| 122 | #include <gst/gst.h> |
---|
| 123 | |
---|
| 124 | int |
---|
| 125 | main(int argc, char *argv[]) |
---|
| 126 | { |
---|
| 127 | GstXML *xml; |
---|
| 128 | GstElement *bin; |
---|
| 129 | gboolean ret; |
---|
| 130 | |
---|
| 131 | gst_init (&argc, &argv); |
---|
| 132 | |
---|
| 133 | xml = gst_xml_new (); |
---|
| 134 | |
---|
| 135 | ret = gst_xml_parse_file(xml, "xmlTest.gst", NULL); |
---|
| 136 | g_assert (ret == TRUE); |
---|
| 137 | |
---|
| 138 | bin = gst_xml_get_element (xml, "bin"); |
---|
| 139 | g_assert (bin != NULL); |
---|
| 140 | |
---|
| 141 | gst_element_set_state (bin, GST_STATE_PLAYING); |
---|
| 142 | |
---|
| 143 | while (gst_bin_iterate(GST_BIN(bin))); |
---|
| 144 | |
---|
| 145 | gst_element_set_state (bin, GST_STATE_NULL); |
---|
| 146 | |
---|
| 147 | exit (0); |
---|
| 148 | } |
---|
| 149 | </programlisting> |
---|
| 150 | <para> |
---|
| 151 | gst_xml_get_element (xml, "name") can be used to get a specific element |
---|
| 152 | from the XML file. |
---|
| 153 | </para> |
---|
| 154 | <para> |
---|
| 155 | gst_xml_get_topelements (xml) can be used to get a list of all toplevel elements |
---|
| 156 | in the XML file. |
---|
| 157 | </para> |
---|
| 158 | <para> |
---|
| 159 | In addition to loading a file, you can also load a from a xmlDocPtr and |
---|
| 160 | an in memory buffer using gst_xml_parse_doc and gst_xml_parse_memory |
---|
| 161 | respectively. Both of these methods return a gboolean indicating |
---|
| 162 | success or failure of the requested action. |
---|
| 163 | </para> |
---|
| 164 | </sect1> |
---|
| 165 | <sect1 id="section-xml-custom"> |
---|
| 166 | <title>Adding custom XML tags into the core XML data</title> |
---|
| 167 | |
---|
| 168 | <para> |
---|
| 169 | It is possible to add custom XML tags to the core XML created with |
---|
| 170 | gst_xml_write. This feature can be used by an application to add more |
---|
| 171 | information to the save plugins. The editor will for example insert |
---|
| 172 | the position of the elements on the screen using the custom XML tags. |
---|
| 173 | </para> |
---|
| 174 | <para> |
---|
| 175 | It is strongly suggested to save and load the custom XML tags using |
---|
| 176 | a namespace. This will solve the problem of having your XML tags |
---|
| 177 | interfere with the core XML tags. |
---|
| 178 | </para> |
---|
| 179 | <para> |
---|
| 180 | To insert a hook into the element saving procedure you can link |
---|
| 181 | a signal to the GstElement using the following piece of code: |
---|
| 182 | </para> |
---|
| 183 | <programlisting> |
---|
| 184 | xmlNsPtr ns; |
---|
| 185 | |
---|
| 186 | ... |
---|
| 187 | ns = xmlNewNs (NULL, "http://gstreamer.net/gst-test/1.0/", "test"); |
---|
| 188 | ... |
---|
| 189 | thread = gst_element_factory_make ("thread", "thread"); |
---|
| 190 | g_signal_connect (G_OBJECT (thread), "object_saved", |
---|
| 191 | G_CALLBACK (object_saved), g_strdup ("decoder thread")); |
---|
| 192 | ... |
---|
| 193 | </programlisting> |
---|
| 194 | <para> |
---|
| 195 | When the thread is saved, the object_save method will be called. Our example |
---|
| 196 | will insert a comment tag: |
---|
| 197 | </para> |
---|
| 198 | <programlisting> |
---|
| 199 | static void |
---|
| 200 | object_saved (GstObject *object, xmlNodePtr parent, gpointer data) |
---|
| 201 | { |
---|
| 202 | xmlNodePtr child; |
---|
| 203 | |
---|
| 204 | child = xmlNewChild (parent, ns, "comment", NULL); |
---|
| 205 | xmlNewChild (child, ns, "text", (gchar *)data); |
---|
| 206 | } |
---|
| 207 | </programlisting> |
---|
| 208 | <para> |
---|
| 209 | Adding the custom tag code to the above example you will get an XML file |
---|
| 210 | with the custom tags in it. Here's an excerpt: |
---|
| 211 | </para> |
---|
| 212 | <programlisting> |
---|
| 213 | ... |
---|
| 214 | <gst:element> |
---|
| 215 | <gst:name>thread</gst:name> |
---|
| 216 | <gst:type>thread</gst:type> |
---|
| 217 | <gst:version>0.1.0</gst:version> |
---|
| 218 | ... |
---|
| 219 | </gst:children> |
---|
| 220 | <test:comment> |
---|
| 221 | <test:text>decoder thread</test:text> |
---|
| 222 | </test:comment> |
---|
| 223 | </gst:element> |
---|
| 224 | ... |
---|
| 225 | </programlisting> |
---|
| 226 | <para> |
---|
| 227 | To retrieve the custom XML again, you need to attach a signal to |
---|
| 228 | the GstXML object used to load the XML data. You can then parse your |
---|
| 229 | custom XML from the XML tree whenever an object is loaded. |
---|
| 230 | </para> |
---|
| 231 | |
---|
| 232 | <para> |
---|
| 233 | We can extend our previous example with the following piece of |
---|
| 234 | code. |
---|
| 235 | </para> |
---|
| 236 | |
---|
| 237 | <programlisting> |
---|
| 238 | xml = gst_xml_new (); |
---|
| 239 | |
---|
| 240 | g_signal_connect (G_OBJECT (xml), "object_loaded", |
---|
| 241 | G_CALLBACK (xml_loaded), xml); |
---|
| 242 | |
---|
| 243 | ret = gst_xml_parse_file (xml, "xmlTest.gst", NULL); |
---|
| 244 | g_assert (ret == TRUE); |
---|
| 245 | </programlisting> |
---|
| 246 | |
---|
| 247 | <para> |
---|
| 248 | Whenever a new object has been loaded, the xml_loaded function will |
---|
| 249 | be called. This function looks like: |
---|
| 250 | </para> |
---|
| 251 | <programlisting> |
---|
| 252 | static void |
---|
| 253 | xml_loaded (GstXML *xml, GstObject *object, xmlNodePtr self, gpointer data) |
---|
| 254 | { |
---|
| 255 | xmlNodePtr children = self->xmlChildrenNode; |
---|
| 256 | |
---|
| 257 | while (children) { |
---|
| 258 | if (!strcmp (children->name, "comment")) { |
---|
| 259 | xmlNodePtr nodes = children->xmlChildrenNode; |
---|
| 260 | |
---|
| 261 | while (nodes) { |
---|
| 262 | if (!strcmp (nodes->name, "text")) { |
---|
| 263 | gchar *name = g_strdup (xmlNodeGetContent (nodes)); |
---|
| 264 | g_print ("object %s loaded with comment '%s'\n", |
---|
| 265 | gst_object_get_name (object), name); |
---|
| 266 | } |
---|
| 267 | nodes = nodes->next; |
---|
| 268 | } |
---|
| 269 | } |
---|
| 270 | children = children->next; |
---|
| 271 | } |
---|
| 272 | } |
---|
| 273 | </programlisting> |
---|
| 274 | <para> |
---|
| 275 | As you can see, you'll get a handle to the GstXML object, the |
---|
| 276 | newly loaded GstObject and the xmlNodePtr that was used to create |
---|
| 277 | this object. In the above example we look for our special tag inside |
---|
| 278 | the XML tree that was used to load the object and we print our |
---|
| 279 | comment to the console. |
---|
| 280 | </para> |
---|
| 281 | </sect1> |
---|
| 282 | |
---|
| 283 | </chapter> |
---|