advanced-interfaces.xml 16.1 KB
Newer Older
1
2
3
<chapter id="cha-advanced-interfaces">
  <title>Interfaces</title>
  <para>
4
5
6
7
    Previously, in the chapter <xref linkend="cha-building-args"/>, we have
    introduced the concept of GObject properties of controlling an element's
    behaviour. This is a very powerful, but has two big disadvantage: firstly,
    it is too generic, and secondly, it isn't dynamic.
8
  </para>
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
  <para>
    The first disadvantage has to do with customizability of the end-user
    interface that will be built to control the element. Some properties are
    more important than others. Some integer properties are better shown in a
    spin-button widget, whereas others would be better represented by a slider
    widget. Such things are not possible because the UI has no actual meaning
    in the application. A UI widget that stands for a bitrate property is the
    same as an UI widget that stands for the size of a video, as long as both
    are of the same <classname>GParamSpec</classname> type. Another problem,
    related to the one about parameter important, is that things like parameter
    grouping, function grouping or anything to make parameters coherent, is not
    really possible.
  </para>
  <para>
    The second argument against parameters are that they are not dynamic. In
    many cases, the allowed values for a property are not fixed, but depend
    on things that can only be detected at run-time. The names of inputs for
    a TV card in a video4linux source element, for example, can only be
    retrieved from the kernel driver when we've opened the device; this only
    happens when the element goes into the READY state. This means that we
    cannot create an enum property type to show this to the user.
  </para>
  <para>
    The solution to those problems is to create very specialized types of
    controls for certain often-used controls. We use the concept of interfaces
    to achieve this. The basis of this all is the glib
    <classname>GTypeInterface</classname> type. For each case where we think
    it's useful, we've created interfaces which can be implemented by elements
    at their own will. We've also created a small extension to
    <classname>GTypeInterface</classname> (which is static itself, too) which
    allows us to query for interface availability based on runtime properties.
    This extension is called <classname>GstImplementsInterface</classname>.
  </para>
42
43
44
45
46
47
48
  <para>
    One important note: interfaces do <emphasis>not</emphasis> replace
    properties. Rather, interfaces should be built <emphasis>next to</emphasis>
    properties. There are two important reasons for this. Firstly, properties
    can be saved in XML files. Secondly, properties can be specified on the
    commandline (<filename>gst-launch</filename>).
  </para>
49
50
51
52

  <sect1 id="sect1-iface-general" xreflabel="How to Implement Interfaces">
    <title>How to Implement Interfaces</title>
    <para>
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
      Implementing interfaces is intiated in the <function>_get_type ()</function>
      of your element. You can register one or more interfaces after having
      registered the type itself. Some interfaces have dependencies on other
      interfaces or can only be registered by certain types of elements. You
      will be notified of doing that wrongly when using the element: it will
      quit with failed assertions, which will explain what went wrong. In the
      case of GStreamer, the only dependency that <emphasis>some</emphasis>
      interfaces have is <classname>GstImplementsInterface</classname>. Per
      interface, we will indicate clearly when it depends on this extension.
      If it does, you need to register support for <emphasis>that</emphasis>
      interface before registering support for the interface that you're
      wanting to support. The example below explains how to add support for a
      simple interface with no further dependencies. For a small explanation
      on <classname>GstImplementsInterface</classname>, see the next section
      about the mixer interface: <xref linkend="sect1-iface-mixer"/>.
68
    </para>
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
    <programlisting>
static void	gst_my_filter_some_interface_init	(GstSomeInterface *iface);

GType
gst_my_filter_get_type (void)
{
  static GType my_filter_type = 0;
                                                                                
  if (!my_filter_type) {
    static const GTypeInfo my_filter_info = {
      sizeof (GstMyFilterClass),
      (GBaseInitFunc) gst_my_filter_base_init,
      NULL,
      (GClassInitFunc) gst_my_filter_class_init,
      NULL,
      NULL,
      sizeof (GstMyFilter),
      0,
      (GInstanceInitFunc) gst_my_filter_init
    };
    static const GInterfaceInfo some_interface_info = {
      (GInterfaceInitFunc) gst_my_filter_some_interface_init,
      NULL,
      NULL
    };

    my_filter_type =
	g_type_register_static (GST_TYPE_MY_FILTER,
				"GstMyFilter",
				&amp;my_filter_info, 0);
    g_type_add_interface_static (my_filter_type,
				 GST_TYPE_SOME_INTERFACE,
                                 &amp;some_interface_info);
  }

  return my_filter_type;
}

static void
gst_my_filter_some_interface_init (GstSomeInterface *iface)
{
  /* here, you would set virtual function pointers in the interface */
}
    </programlisting>
113
114
115
116
117
  </sect1>

  <sect1 id="sect1-iface-mixer" xreflabel="Mixer Interface">
    <title>Mixer Interface</title>
    <para>
118
119
120
121
122
123
124
125
126
127
      The goal of the mixer interface is to provide a simple yet powerful API
      to applications for audio hardware mixer/volume control. Most soundcards
      have hardware mixers, where volume can be changed, they can be muted,
      inputs can be modified to mix their content into what will be read from
      the device by applications (in our case: audio source plugins). The
      mixer interface is the way to control those. The mixer interface can
      also be used for volume control in software (e.g. the <quote>volume</quote>
      element). The end goal of this interface is to allow development of
      hardware volume control applications and for the control of audio volume
      and input/output settings.
128
    </para>
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    <para>
      The mixer interface requires the <classname>GstImplementsInterface</classname>
      interface to be implemented by the element. The example below will
      feature both, so it serves as an example for the
      <classname>GstImplementsInterface</classname>, too. In the
      <classname>GstImplementsInterface</classname>, it is required to set a
      function pointer for the <function>supported ()</function> function. If
      you don't, this function will always return FALSE (default
      implementation) and the mixer interface implementation will not work. For
      the mixer interface, the only required function is
      <function>list_tracks ()</function>. All other function pointers in the
      mixer interface are optional, although it is strongly recommended to set
      function pointers for at least the <function>get_volume ()</function> and
      <function>set_volume ()</function> functions. The API reference for this
      interface documents the goal of each function, so we will limit ourselves
      to the implementation here.
    </para>
    <programlisting>
147
148
#include &lt;gst/mixer/mixer.h&gt;

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
typedef struct _GstMyFilter {
[..]
  gint volume;
  GList *tracks;
} GstMyFilter;

static void	gst_my_filter_implements_interface_init	(GstImplementsInterfaceClass *iface);
static void	gst_my_filter_mixer_interface_init	(GstMixerClass *iface);

GType
gst_my_filter_get_type (void)
{
[..]
    static const GInterfaceInfo implements_interface_info = {
      (GInterfaceInitFunc) gst_my_filter_implements_interface_init,
      NULL,
      NULL
    };
    static const GInterfaceInfo mixer_interface_info = {
      (GInterfaceInitFunc) gst_my_filter_mixer_interface_init,
      NULL,
      NULL
    };
[..]
    g_type_add_interface_static (my_filter_type,
				 GST_TYPE_IMPLEMENTS_INTERFACE,
				 &amp;implements_interface_info);
    g_type_add_interface_static (my_filter_type,
				 GST_TYPE_MIXER,
				 &amp;mixer_interface_info);
[..]
}

static void
gst_my_filter_init (GstMyFilter *filter)
{
  GstMixerTrack *track = NULL;
[..]
  filter->volume = 100;
  filter->tracks = NULL;
  track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
  track->label = g_strdup ("MyTrack");
  track->num_channels = 1;
  track->min_volume = 0;
  track->max_volume = 100;
  track->flags = GST_MIXER_TRACK_SOFTWARE;
  filter->tracks = g_list_append (filter->tracks, track);
}

static gboolean
gst_my_filter_interface_supported (GstImplementsInterface *iface,
				   GType                   iface_type)
{
  g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE);

  /* for the sake of this example, we'll always support it. However, normally,
   * you would check whether the device you've opened supports mixers. */
  return TRUE;
}

static void
gst_my_filter_implements_interface_init (GstImplementsInterfaceClass *iface)
{
  iface->supported = gst_my_filter_interface_supported;
}

/*
 * This function returns the list of support tracks (inputs, outputs)
 * on this element instance. Elements usually build this list during
 * _init () or when going from NULL to READY.
 */

static const GList *
gst_my_filter_mixer_list_tracks (GstMixer *mixer)
{
  GstMyFilter *filter = GST_MY_FILTER (mixer);

  return filter->tracks;
}

/*
 * Set volume. volumes is an array of size track->num_channels, and
 * each value in the array gives the wanted volume for one channel
 * on the track.
 */

static void
gst_my_filter_mixer_set_volume (GstMixer      *mixer,
				GstMixerTrack *track,
				gint          *volumes)
{
  GstMyFilter *filter = GST_MY_FILTER (mixer);

  filter->volume = volumes[0];

  g_print ("Volume set to %d\n", filter->volume);
}

static void
gst_my_filter_mixer_get_volume (GstMixer      *mixer,
				GstMixerTrack *track,
				gint          *volumes)
{
  GstMyFilter *filter = GST_MY_FILTER (mixer);

  volumes[0] = filter->volume;
}

static void
gst_my_filter_mixer_interface_init (GstMixerClass *iface)
{
  /* the mixer interface requires a definition of the mixer type:
   * hardware or software? */
  GST_MIXER_TYPE (iface) = GST_MIXER_SOFTWARE;

  /* virtual function pointers */
  iface->list_tracks = gst_my_filter_mixer_list_tracks;
  iface->set_volume  = gst_my_filter_mixer_set_volume;
  iface->get_volume  = gst_my_filter_mixer_get_volume;
}
    </programlisting>
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  </sect1>

  <sect1 id="sect1-iface-tuner" xreflabel="Tuner Interface">
    <title>Tuner Interface</title>
    <para>
      WRITEME
    </para>
  </sect1>

  <sect1 id="sect1-iface-colorbalance" xreflabel="Color Balance Interface">
    <title>Color Balance Interface</title>
    <para>
      WRITEME
    </para>
  </sect1>

  <sect1 id="sect1-iface-propprobe" xreflabel="Property Probe Interface">
    <title>Property Probe Interface</title>
    <para>
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
      Property probing is a generic solution to the problem that properties'
      value lists in an enumeration are static. We've shown enumerations in
      <xref linkend="cha-building-args"/>. Property probing tries to accomplish
      a goal similar to enumeration lists: to have a limited, explicit list of
      allowed values for a property. There are two differences between
      enumeration lists and probing. Firstly, enumerations only allow strings
      as values; property probing works for any value type. Secondly, the
      contents of a probed list of allowed values may change during the life
      of an element. The contents of a enumeraiton list are static. Crrently,
      property probing is being used for detection of devices (e.g. for OSS
      elements, Video4linux elements, etc.). It could - in theory - be used
      for any property, though.
    </para>
    <para>
      Property probing stores the list of allowed (or recommended) values in a
      <classname>GValueArray</classname> and returns that to the user.
      <classname>NULL</classname> is a valid return value, too. The process of
      property probing is separated over two virtual functions: one for probing
      the property to create a <classname>GValueArray</classname>, and one to
      retrieve the current <classname>GValueArray</classname>. Those two are
      separated because probing might take a long time (several seconds). Also,
      this simpliies interface implementation in elements. For the application,
      there are functions that wrap those two. For more information on this,
      have a look at the API reference for the
      <classname>GstPropertyProbe</classname> interface.
    </para>
    <para>
      Below is a example of property probing for the audio filter element; it
      will probe for allowed values for the <quote>silent</quote> property.
      Indeed, this value is a <classname>gboolean</classname> so it doesn't
      make much sense. Then again, it's only an example.
    </para>
    <programlisting>
#include &lt;gst/propertyprobe/propertyprobe.h&gt;

static void	gst_my_filter_probe_interface_init	(GstPropertyProbeInterface *iface);

GType
gst_my_filter_get_type (void)
{
[..]
    static const GInterfaceInfo probe_interface_info = {
      (GInterfaceInitFunc) gst_my_filter_probe_interface_init,
      NULL,
      NULL
    };
[..]
    g_type_add_interface_static (my_filter_type,
				 GST_TYPE_PROPERTY_PROBE,
				 &amp;probe_interface_info);
[..]
}

static const GList *
gst_my_filter_probe_get_properties (GstPropertyProbe *probe)
{
  GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
  static GList *props = NULL;

  if (!props) {
    GParamSpec *pspec;

    pspec = g_object_class_find_property (klass, "silent");
    props = g_list_append (props, pspec);
  }

  return props;
}

static gboolean
gst_my_filter_probe_needs_probe (GstPropertyProbe *probe,
				 guint             prop_id,
				 const GParamSpec *pspec)
{
  gboolean res = FALSE;

  switch (prop_id) {
    case ARG_SILENT:
      res = FALSE;
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }

  return res;
}

static void
gst_my_filter_probe_probe_property (GstPropertyProbe *probe,
				    guint             prop_id,
				    const GParamSpec *pspec)
{
  switch (prop_id) {
    case ARG_SILENT:
      /* don't need to do much here... */
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }
}

static GValueArray *
gst_my_filter_get_silent_values (GstMyFilter *filter)
{
  GValueArray *array = g_value_array_new (2);
  GValue value = { 0 };

  g_value_init (&amp;value, G_TYPE_BOOLEAN);

  /* add TRUE */
  g_value_set_boolean (&amp;value, TRUE);
  g_value_array_append (array, &amp;value);

  /* add FALSE */
  g_value_set_boolean (&amp;value, FALSE);
  g_value_array_append (array, &amp;value);

  g_value_unset (&amp;value);

  return array;
}

static GValueArray *
gst_my_filter_probe_get_values (GstPropertyProbe *probe,
				guint             prop_id,
				const GParamSpec *pspec)
{
  GstMyFilter *filter = GST_MY_FILTER (probe);
  GValueArray *array = NULL;

  switch (prop_id) {
    case ARG_SILENT:
      array = gst_my_filter_get_silent_values (filter);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
      break;
  }

  return array;
}

static void
gst_my_filter_probe_interface_init (GstPropertyProbeInterface *iface)
{
  iface->get_properties = gst_my_filter_probe_get_properties;
  iface->needs_probe    = gst_my_filter_probe_needs_probe;
  iface->probe_property = gst_my_filter_probe_probe_property;
  iface->get_values     = gst_my_filter_probe_get_values;
}
    </programlisting>
    <para>
      You don't need to support any functions for getting or setting values.
      All that is handled via the standard <classname>GObject</classname>
      <function>_set_property ()</function> and <function>_get_property ()</function>
      functions.
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
    </para>
  </sect1>

  <sect1 id="sect1-iface-profile" xreflabel="Profile Interface">
    <title>Profile Interface</title>
    <para>
      WRITEME
    </para>
  </sect1>
                                                                                
  <sect1 id="sect1-iface-xoverlay" xreflabel="X Overlay Interface">
    <title>X Overlay Interface</title>
    <para>
      WRITEME
    </para>
  </sect1>

  <sect1 id="sect1-iface-navigation" xreflabel="Navigation Interface">
    <title>Navigation Interface</title>
    <para>
      WRITEME
    </para>
  </sect1>
470
</chapter>