Add GInterface

Original commit message from CVS:
Add GInterface
This commit is contained in:
Ronald S. Bultje 2003-09-04 21:03:12 +00:00
parent b79680d0b2
commit 8ff69a6cc8

View file

@ -47,7 +47,7 @@ Obviously, a pipeline can be a fallback for an interface. Imagine
that we're looking for an audio sink that exposes a mixer, but our
fakesink audio output doesn't ("I wonder why"). We could then create
a pipeline with the volume element in it to "fake" a mixer. Ideally,
the volume element would implement a mixer itself.
the volume element would implement a mixer interface itself.
How are we going to do that in programmatic way? We currently use
properties. Their huge advantage is that we do not need to care
@ -65,16 +65,12 @@ the idea of embedding interfaces (dynamically, of course) in the
GstElement object. Think of an object being able to register an
indefinate number of interfaces per object instance, and a client
application could then enumerate interfaces and instantiate one.
The API would then look like this:
void gst_element_register_interface (GstElement *element,
const gchar *name,
GstInterfaceFunc func);
const GList *gst_element_list_interfaces (GstElement *element);
GstInterface *gst_element_get_interface (GstElement *element,
const gchar *name);
Glib gives us GInterface for this purpose. The disadvantage of
this is that it's on a per-class basis, not a per-instance basis.
This is a problem in case of elements where it depends on several
properties whether it supports an interface or not. This can be
solved by simply making one generic virtual function "supported ()"
in a generic derived object of GInterface (GstInterface?).
GstInterface is then a generic thing that is inherited by specific
interfaces (see examples). Obviously, the client will need to know
@ -89,19 +85,12 @@ useful currently, so this'd make them a lot more interesting).
These interfaces inherit from GstInterface. The functions that
are needed, can be provided through a class object. The element is
then responsible for storing variables and so on. gstvideo/audio
provides wrapper functions for the class functions.
provides wrapper functions for the class functions. That's also how
glib suggest us to use GInterfaces.
For the plugin, it's then as simple as can be. The class_init
function sets the virtual functions in the interface class object,
and the instance_init function registers the object per created
element. The get_interface() handler refs this interface and
returns it. The application unrefs it when it's done. The
appropriate functions will be called by the application when it
thinks it needs to. Perfectly simple!
For applictions, it's even simpler. Request an interface and use
it as documented. When you're done, unref it. It's just like
elements: simple!
Plugin and application then handle and retrieve interfaces as
documented in the glib documentation, which is available at:
http://www.gnome.org/~mathieu/gobject/main.html
So the most important part left is to document the interfaces
and make sure all elements exporting them work equally. For this,
@ -111,13 +100,41 @@ I'll give two examples.
===========
typedef struct _GstInterface {
GObject object;
/* dummy */
} GstInterface;
typedef struct _GstInterfaceClass {
GObjectClass klass;
GTypeInterface parent;
gboolean (* supported) (GstInterface *iface);
} GstInterfaceClass;
There would probably be a convenience function that checks
a specific interface's implementation (glib allows for this)
and checks for ->supported () to be set and to return TRUE:
gboolean
gst_element_implements_interface (GstElement *element,
GType iface_type)
{
if (G_TYPE_CHECK_INSTANCE_TYPE (G_OBJECT (element),
type)) {
GstInterface *iface;
GstInterfaceClass *ifclass;
iface = G_TYPE_CHECK_INSTANCE_CAST (G_OBJECT (element),
type, GstInterface)
ifclass = GST_INTERFACE_GET_CLASS (iface);
if (ifclass->supported != NULL &&
ifclass->supported (iface) == TRUE) {
return TRUE;
}
}
return FALSE;
}
4a) mixer
---------
A mixer is a way of controlling volume and input/output channels.
@ -186,50 +203,20 @@ typedef struct _GstOssMixerClass {
[..]
static void
gst_ossmixer_class_init (GstOssMixerClass *klass)
{
GstMixerClass *mix_klass = (GstMixerClass *) klass;
[.. set virtual functions to their oss/mixer counterparts here ..]
}
[..]
static void
gst_osssrc_init (GstOssSrc *osssrc)
{
GstOssMixer *mixer = GST_OSS_MIXER (osssrc);
[..]
gst_element_register_interface (GST_ELEMENT (osssrc),
"mixer",
gst_osssrc_get_interface);
mixer->element = GST_ELEMENT (osssrc);
[..]
}
static GstInterface *
gst_osssrc_get_interface (GstElement *element,
const gchar *name)
{
GstOssMixer *mixer;
The rest is done automatically, as described in the already-
mentioned glib documentation for GInterface.
g_assert (strcmp (name, "mixer") == 0);
mixer = g_object_new (GST_TYPE_OSS_MIXER, NULL);
mixer->element = element;
[..]
return (GstInterface *) mixer;
}
And yes, that's quite a piece of code, but you didn't expect
that we could make a mixer in five lines of code, did you?
However, applications now *can*!
There might be some refcounting issues here: get_interface ()
should ref () the element, and we should set a mixer dispose
handler to unref () it again. Then, too, we could add a pointer
to the file descriptor in the osssrc/osssink, too, and we'd
have full access to the device.
We could add a pointer to the file descriptor in the
osssrc/osssink, too, and we'd have full access to the device.
However, that's implementation. Let's first worry about general
design.