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> |
---|