Table of Contents
Previously, in the chapter Adding Properties, we have introduced the concept of GObject properties of controlling an element's behaviour. This is very powerful, but it has two big disadvantages: first of all, it is too generic, and second, it isn't dynamic.
The first disadvantage is related to the 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 represents a bitrate property is the
same as a UI widget that represents the size of a video, as long as both
are of the same GParamSpec
type. Another problem,
is that things like parameter grouping, function grouping, or parameter
coupling are not
really possible.
The second problem with 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 runtime. 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.
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
GTypeInterface
type. For each case where we think
it's useful, we've created interfaces which can be implemented by elements
at their own will.
One important note: interfaces do not replace
properties. Rather, interfaces should be built next to
properties. There are two important reasons for this. First of all,
properties can be more easily introspected. Second, properties can be
specified on the commandline (gst-launch
).
Implementing interfaces is initiated in the _get_type ()
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.
If it does, you need to register support for that
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.
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), NULL, 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_ELEMENT, "GstMyFilter", &my_filter_info, 0); g_type_add_interface_static (my_filter_type, GST_TYPE_SOME_INTERFACE, &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 */ }
Or more conveniently:
static void gst_my_filter_some_interface_init (GstSomeInterface *iface); G_DEFINE_TYPE_WITH_CODE (GstMyFilter, gst_my_filter,GST_TYPE_ELEMENT, G_IMPLEMENT_INTERFACE (GST_TYPE_SOME_INTERFACE, gst_my_filter_some_interface_init));