1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
<chapter id="cha-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="sec-xml-write">
<title>Turning GstElements into XML</title>
<para>
We create a simple pipeline and save it to disk with gst_xml_write (). The following
code constructs an mp3 player pipeline with two threads and finaly writes it to disk.
use this program with one argument: the mp3 file on disk.
</para>
<programlisting>
#include <stdlib.h>
#include <gst/gst.h>
gboolean playing;
int
main (int argc, char *argv[])
{
GstElement *filesrc, *audiosink, *queue, *queue2, *parse, *decode;
GstElement *bin;
GstElement *thread, *thread2;
gst_init (&argc,&argv);
if (argc != 2) {
g_print ("usage: %s <filename>\n", argv[0]);
exit (-1);
}
/* create a new thread to hold the elements */
thread = gst_elementfactory_make ("thread", "thread");
g_assert (thread != NULL);
thread2 = gst_elementfactory_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_elementfactory_make ("filesrc", "disk_source");
g_assert (filesrc != NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
queue = gst_elementfactory_make ("queue", "queue");
queue2 = gst_elementfactory_make ("queue", "queue2");
/* and an audio sink */
audiosink = gst_elementfactory_make ("audiosink", "play_audio");
g_assert (audiosink != NULL);
parse = gst_elementfactory_make ("mp3parse", "parse");
decode = gst_elementfactory_make ("mpg123", "decode");
/* add objects to the main bin */
gst_bin_add (GST_BIN (bin), filesrc);
gst_bin_add (GST_BIN (bin), queue);
gst_bin_add (GST_BIN (thread), parse);
gst_bin_add (GST_BIN (thread), decode);
gst_bin_add (GST_BIN (thread), queue2);
gst_bin_add (GST_BIN (thread2), audiosink);
gst_pad_connect (gst_element_get_pad (filesrc,"src"),
gst_element_get_pad (queue,"sink"));
gst_pad_connect (gst_element_get_pad (queue,"src"),
gst_element_get_pad (parse,"sink"));
gst_pad_connect (gst_element_get_pad (parse,"src"),
gst_element_get_pad (decode,"sink"));
gst_pad_connect (gst_element_get_pad (decode,"src"),
gst_element_get_pad (queue2,"sink"));
gst_pad_connect (gst_element_get_pad (queue2,"src"),
gst_element_get_pad (audiosink,"sink"));
gst_bin_add (GST_BIN (bin), thread);
gst_bin_add (GST_BIN (bin), thread2);
// write the bin to disk
xmlSaveFile ("xmlTest.gst", gst_xml_write (GST_ELEMENT (bin)));
exit (0);
}
</programlisting>
<para>
The most important line is:
</para>
<programlisting>
xmlSaveFile ("xmlTest.gst", gst_xml_write (GST_ELEMENT (bin)));
</programlisting>
<para>
gst_xml_write () will turn the given element into and xmlDocPtr that
can be saved with the xmlSaveFile () function found in the gnome-xml
package. The result is an XML file named xmlTest.gst.
</para>
<para>
The complete element hierarchy will be saved along with the inter element
pad connections 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="sec-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
respectivily. both of these methods return a gboolean indicating success
or failure of the requested action.
</para>
</sect1>
<sect1 id="sec-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 connect
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_elementfactory_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 caled. 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>
|