2003-08-28 20:48:54 +00:00
|
|
|
INTERFACES & ELEMENTS
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
1) Introduction
|
|
|
|
===============
|
|
|
|
Interfaces are descriptions on how to handle an object, without actually
|
|
|
|
implementing the object. This allows for multiple objects to be instantiated
|
|
|
|
based on this interface. Each of them can then be handled equally by an
|
|
|
|
application.
|
|
|
|
Glib, apparently (unchecked), has a way of creating interfaces, probably
|
|
|
|
by means of a class struct without actually defining the object. The object,
|
|
|
|
then, does not define a class and these two add up. Benjamin knows more
|
|
|
|
about interfaces, I didn't study interfaces & glib too deeply, yet. I know
|
|
|
|
them just from Java.
|
|
|
|
Interfaces are cool! It allows for some sort of random element creation
|
|
|
|
without needing to link to the implementation. This is similar to how
|
|
|
|
GStreamer currently handles media plugins. GStreamer itself could be seen
|
|
|
|
as an interface too, in that respect.
|
|
|
|
|
|
|
|
2) So why do we need interfaces?
|
|
|
|
================================
|
|
|
|
Because GStreamer doesn't handle it all. GStreamer in itself is a media
|
|
|
|
framework for streams of data from one element to the next. There's lots
|
|
|
|
of things that's media-related, but not handled in this description.
|
|
|
|
Several examples will probably clarify this: think of the Xvideo output
|
|
|
|
plugin. We can create an overlay here (Xv-based), and we currently control
|
|
|
|
this X-connection using glib properties. However, what property name is
|
|
|
|
associated with what control? And does it work the same as v4lsrc's
|
|
|
|
overlay image control?
|
|
|
|
The same goes for a mixer, for image control, audio control, and probably
|
|
|
|
a lot more. The general idea is simple: *this needs to be documented*.
|
|
|
|
But properties aren't all - they simply cannot do this all. Some things
|
|
|
|
cannot be described in a simple one-argument property thing. Of course,
|
|
|
|
we could give a pointer to a struct as argument, but that's merely a hack
|
|
|
|
and requires both plugin and app to know the ABI of the struct. This
|
|
|
|
kills the whole idea of making the plugin independent of the app.
|
|
|
|
In short: we want interfaces for this.
|
|
|
|
|
|
|
|
3) How to integrate an interface in GStreamer
|
|
|
|
=============================================
|
|
|
|
Let us start with some starting point: an interface is associated
|
|
|
|
with an element. It is a feature exported by that specific element,
|
|
|
|
not by a pipeline or anything more complex. Pipelines are already
|
|
|
|
handled just fine by GStreamer (or you wouldn't be reading all
|
|
|
|
this).
|
|
|
|
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.
|
|
|
|
|
|
|
|
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
|
|
|
|
about adding new functions or whatever. Their disadvantage is that
|
|
|
|
they're limited to one argument. Anything more complex requires
|
|
|
|
app/plugin knowledge about the shared data, and that defeats the
|
|
|
|
point of them: to have no dependency on each other. This could be
|
|
|
|
solved partially by using action signals, but that makes the whole
|
|
|
|
picture quite complex (since you use multiple methods for doing one
|
|
|
|
simple thing). Also, they are quite slow compared to functions
|
|
|
|
because of the table lookups. In short: it'd work, but I'm not in
|
|
|
|
facour of it...
|
|
|
|
OK, so an element exposes interfaces. This allows us to think of
|
|
|
|
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);
|
|
|
|
|
|
|
|
GstInterface is then a generic thing that is inherited by specific
|
|
|
|
interfaces (see examples). Obviously, the client will need to know
|
|
|
|
about the ABI/API of this struct, but that'll happen either way.
|
|
|
|
Surely, there needs to binary linkage, but I don't consider that a
|
|
|
|
bad thing. It does improve performance compared to action signals!
|
|
|
|
|
|
|
|
So an element contains interfaces. But where are these interfaces
|
|
|
|
described? And who creates them? I suggest that we do that just as
|
|
|
|
we handle gstvideo and gstaudio right now (these libs do *nothing*
|
|
|
|
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.
|
|
|
|
|
|
|
|
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!
|
|
|
|
|
|
|
|
So the most important part left is to document the interfaces
|
|
|
|
and make sure all elements exporting them work equally. For this,
|
|
|
|
I'll give two examples.
|
|
|
|
|
|
|
|
4) Examples
|
|
|
|
===========
|
|
|
|
|
|
|
|
typedef struct _GstInterface {
|
|
|
|
GObject object;
|
|
|
|
} GstInterface;
|
|
|
|
|
|
|
|
typedef struct _GstInterfaceClass {
|
|
|
|
GObjectClass klass;
|
|
|
|
} GstInterfaceClass;
|
|
|
|
|
|
|
|
4a) mixer
|
|
|
|
---------
|
|
|
|
A mixer is a way of controlling volume and input/output channels.
|
|
|
|
This doesn't mean that you control which channel is the subwoofer,
|
|
|
|
all that is supposed to be done automatically. It is really meant
|
|
|
|
as a way of representing system-level volumes and such. It could
|
|
|
|
also be used to turn on/off certain outputs or inputs.
|
|
|
|
As you've noticed, I'm not only talking about output, but also
|
|
|
|
input. Indeed, I want both osssrc *and* osssink to export the
|
|
|
|
same mixer interface! Or at least a very similar one. Volume
|
|
|
|
control works the same for both. You could say that osssrc should
|
|
|
|
enumerate the input channels (such as microphone, line-in). Of
|
|
|
|
course, osssink should not. Or maybe it should, not sure...
|
|
|
|
And alsasink would surely implement the same interface.
|
|
|
|
|
|
|
|
/* This is confusing naming... (i.e. FIXME)
|
|
|
|
* A channel is referred to both as the number of simultaneous
|
|
|
|
* sounds the input can handle as well as the in-/output itself
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct _GstMixerChannel {
|
|
|
|
gchar *label;
|
|
|
|
gint current_num_channels,
|
|
|
|
max_num_channels;
|
|
|
|
} GstMixerChannel;
|
|
|
|
|
|
|
|
typedef struct _GstMixer {
|
|
|
|
GstInterface interface;
|
|
|
|
} GstMixer;
|
|
|
|
|
|
|
|
typedef struct _GstMixerClass {
|
|
|
|
GstInterfaceClass klass;
|
|
|
|
|
|
|
|
/* virtual functions */
|
|
|
|
GList * (* list_channels) (GstMixer *mixer);
|
|
|
|
void (* set_volume) (GstMixer *mixer,
|
|
|
|
GstMixerChannel *channel,
|
|
|
|
gint *volumes);
|
|
|
|
void (* get_volume) (GstMixer *mixer,
|
|
|
|
GstMixerChannel *channel,
|
|
|
|
gint *volumes);
|
|
|
|
void (* set_mute) (GstMixer *mixer,
|
|
|
|
GstMixerChannel *channel,
|
|
|
|
gboolean mute);
|
|
|
|
gboolean (* get_mute) (GstMixer *mixer,
|
|
|
|
GstMixerChannel *channel);
|
|
|
|
} GstMixerClass;
|
|
|
|
|
|
|
|
Name for in the element list: "mixer". Gstaudio provides wrapper
|
|
|
|
functions for each of the class' virtual functions. Possibly also
|
|
|
|
some macros for GST_MIXER_CHANNEL_HAS_FLAG () or _get_channel ().
|
|
|
|
|
2003-08-29 06:50:56 +00:00
|
|
|
How to register these? Let's say that we use osssrc as an example.
|
|
|
|
|
|
|
|
typedef struct _GstOssMixer {
|
|
|
|
GstMixer mixer;
|
|
|
|
|
|
|
|
GstElement *element;
|
|
|
|
|
|
|
|
[.. more? ..]
|
|
|
|
} GstOssMixer;
|
|
|
|
|
|
|
|
typedef struct _GstOssMixerClass {
|
|
|
|
GstMixerClass klass;
|
|
|
|
} 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)
|
|
|
|
{
|
|
|
|
[..]
|
|
|
|
gst_element_register_interface (GST_ELEMENT (osssrc),
|
|
|
|
"mixer",
|
|
|
|
gst_osssrc_get_interface);
|
|
|
|
[..]
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstInterface *
|
|
|
|
gst_osssrc_get_interface (GstElement *element,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
GstOssMixer *mixer;
|
|
|
|
|
|
|
|
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.
|
|
|
|
However, that's implementation. Let's first worry about general
|
|
|
|
design.
|
|
|
|
|
2003-08-28 20:48:54 +00:00
|
|
|
4b) overlay
|
|
|
|
-----------
|
|
|
|
Overlay is used in both in- and output, too. Think of v4lsrc,
|
|
|
|
v4l2src, v4lmjpegsrc, xvideosink - all overlays. But where do
|
|
|
|
we position the overlay window? Control of this can be done at
|
|
|
|
various levels: locational control (over the server, asynchronous)
|
|
|
|
or XID control (but that makes you depend on X and limits the
|
|
|
|
ability to broaden it over to non-X elements such as fbsink).
|
|
|
|
|
|
|
|
However, simplicity *is* an issue here. Do we really care about
|
|
|
|
overlay? In the end, users will have to link against either FB
|
|
|
|
or X anyway, so we might want to create separate interfaces for
|
|
|
|
both. On the other hand, we want to be general too... This is a
|
|
|
|
decision that we need to make as early as possible in this process.
|
|
|
|
|
|
|
|
Let's assume that we take X as a basis. Then, overlay becomes as
|
|
|
|
simple as one function. Possible extendible by providing inputs
|
|
|
|
(like in the mixer) and norms, although that only applies to
|
|
|
|
input-to-analog, not to-digital... Discussion needed here!
|
|
|
|
|
|
|
|
typedef struct _GstOverlayChannel {
|
|
|
|
gchar *label;
|
|
|
|
} GstOverlayChannel;
|
|
|
|
|
|
|
|
typedef struct _GstOverlayNorm {
|
|
|
|
gchar *label;
|
|
|
|
} GstOverlayNorm;
|
|
|
|
|
|
|
|
typedef struct _GstOverlay {
|
|
|
|
GstInterface interface;
|
|
|
|
} GstOverlay;
|
|
|
|
|
|
|
|
typedef struct _GstOverlayClass {
|
|
|
|
GstInterfaceClass klass;
|
|
|
|
|
|
|
|
/* virtual functions */
|
|
|
|
GList * (* list_channels) (GstOverlay *overlay);
|
|
|
|
void (* set_channel) (GstOverlay *overlay,
|
|
|
|
GstOverlayChannel *channel);
|
|
|
|
const gchar * (* get_channel) (GstOverlay *overlay);
|
|
|
|
GList * (* list_norms) (GstOverlay *overlay);
|
|
|
|
void (* set_norm) (GstOverlay *overlay,
|
|
|
|
GstOverlayNorm *norm);
|
|
|
|
const gchar * (* get_norm) (GstOverlay *overlay);
|
|
|
|
void (* set_xwindowid) (GstOverlay *overlay,
|
2003-08-29 06:50:56 +00:00
|
|
|
XID xid);
|
2003-08-28 20:48:54 +00:00
|
|
|
} GstOverlayClass;
|
|
|
|
|
2003-08-29 06:50:56 +00:00
|
|
|
That's all!
|
|
|
|
|
2003-08-28 20:48:54 +00:00
|
|
|
4c) user input
|
|
|
|
--------------
|
|
|
|
And yes, user input could be an interface too. Even better, it
|
|
|
|
should definately be. And wasn't this one of our key issues for
|
|
|
|
0.8.0?
|
|
|
|
|
|
|
|
No code here. Go implement it, lazy ass!
|
|
|
|
|
|
|
|
5) Status of this document
|
|
|
|
==========================
|
|
|
|
This is a proposal, nothing more. Nothing is implemented. Target
|
|
|
|
release is 0.8.0 or any 0.7.x version.
|
|
|
|
|
|
|
|
6) Copyright and blabla
|
|
|
|
=======================
|
|
|
|
(c) Ronald Bultje, 2003 <rbultje@ronald.bitfreak.net> under the
|
|
|
|
terms of the GNU Free Documentation License. See http://www.gnu.org/
|
|
|
|
for details.
|
|
|
|
|
|
|
|
And no, I'm not for hire. ;).
|