2004-01-28 15:51:14 +00:00
|
|
|
<chapter id="chapter-advanced-interfaces">
|
2004-01-27 14:58:21 +00:00
|
|
|
<title>Interfaces</title>
|
|
|
|
<para>
|
2004-01-28 15:51:14 +00:00
|
|
|
Previously, in the chapter <xref linkend="chapter-building-args"/>, we have
|
2004-01-28 09:07:11 +00:00
|
|
|
introduced the concept of GObject properties of controlling an element's
|
2004-11-25 15:45:36 +00:00
|
|
|
behaviour. This is very powerful, but it has two big disadvantages:
|
|
|
|
first of all, it is too generic, and second, it isn't dynamic.
|
2004-01-27 14:58:21 +00:00
|
|
|
</para>
|
2004-01-28 09:07:11 +00:00
|
|
|
<para>
|
2004-11-25 15:45:36 +00:00
|
|
|
The first disadvantage is related to the customizability of the end-user
|
2004-01-28 09:07:11 +00:00
|
|
|
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
|
2004-11-25 15:45:36 +00:00
|
|
|
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
|
2004-01-28 09:07:11 +00:00
|
|
|
are of the same <classname>GParamSpec</classname> type. Another problem,
|
2004-11-25 15:45:36 +00:00
|
|
|
is that things like parameter grouping, function grouping, or parameter
|
|
|
|
coupling are not
|
2004-01-28 09:07:11 +00:00
|
|
|
really possible.
|
|
|
|
</para>
|
|
|
|
<para>
|
2004-11-25 15:45:36 +00:00
|
|
|
The second problem with parameters are that they are not dynamic. In
|
2004-01-28 09:07:11 +00:00
|
|
|
many cases, the allowed values for a property are not fixed, but depend
|
2004-11-25 15:45:36 +00:00
|
|
|
on things that can only be detected at runtime. The names of inputs for
|
2004-01-28 09:07:11 +00:00
|
|
|
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.
|
2004-07-27 15:01:10 +00:00
|
|
|
This extension is called <ulink type="http"
|
|
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
|
|
GstImplementsInterface</classname></ulink>.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
2004-01-28 14:16:59 +00:00
|
|
|
<para>
|
|
|
|
One important note: interfaces do <emphasis>not</emphasis> replace
|
|
|
|
properties. Rather, interfaces should be built <emphasis>next to</emphasis>
|
2004-11-25 15:45:36 +00:00
|
|
|
properties. There are two important reasons for this. First of all,
|
|
|
|
properties
|
|
|
|
can be saved in XML files. Second, properties can be specified on the
|
2004-01-28 14:16:59 +00:00
|
|
|
commandline (<filename>gst-launch</filename>).
|
|
|
|
</para>
|
2004-01-28 09:07:11 +00:00
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-general" xreflabel="How to Implement Interfaces">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>How to Implement Interfaces</title>
|
|
|
|
<para>
|
2004-01-28 12:20:40 +00:00
|
|
|
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>
|
2004-07-27 15:01:10 +00:00
|
|
|
interfaces have is <ulink type="http"
|
|
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
|
|
GstImplementsInterface</classname></ulink>. Per
|
2004-01-28 12:20:40 +00:00
|
|
|
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
|
2004-07-27 15:01:10 +00:00
|
|
|
on <ulink type="http" url="../../gstreamer/html/GstImplementsInterface.html">
|
|
|
|
<classname>GstImplementsInterface</classname></ulink>, see the next section
|
2004-01-28 15:51:14 +00:00
|
|
|
about the mixer interface: <xref linkend="section-iface-mixer"/>.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
2004-01-28 12:20:40 +00:00
|
|
|
<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",
|
|
|
|
&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 */
|
|
|
|
}
|
|
|
|
</programlisting>
|
2004-01-28 09:07:11 +00:00
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-mixer" xreflabel="Mixer Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>Mixer Interface</title>
|
|
|
|
<para>
|
2004-01-28 12:20:40 +00:00
|
|
|
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.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
2004-01-28 12:20:40 +00:00
|
|
|
<para>
|
2004-07-27 15:01:10 +00:00
|
|
|
The mixer interface requires the <ulink type="http"
|
|
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
|
|
GstImplementsInterface</classname></ulink>
|
2004-01-28 12:20:40 +00:00
|
|
|
interface to be implemented by the element. The example below will
|
2004-07-27 15:01:10 +00:00
|
|
|
feature both, so it serves as an example for the <ulink type="http"
|
|
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
|
|
GstImplementsInterface</classname></ulink>, too. In this
|
|
|
|
interface, 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
|
2004-01-28 12:20:40 +00:00
|
|
|
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>
|
2004-01-30 12:00:16 +00:00
|
|
|
<para>
|
|
|
|
The following example shows a mixer implementation for a software N-to-1
|
|
|
|
element. It does not show the actual process of stream mixing, that is
|
|
|
|
far too complicated for this guide.
|
|
|
|
</para>
|
2004-01-28 12:20:40 +00:00
|
|
|
<programlisting>
|
2004-01-28 14:16:59 +00:00
|
|
|
#include <gst/mixer/mixer.h>
|
|
|
|
|
2004-01-28 12:20:40 +00:00
|
|
|
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,
|
|
|
|
&implements_interface_info);
|
|
|
|
g_type_add_interface_static (my_filter_type,
|
|
|
|
GST_TYPE_MIXER,
|
|
|
|
&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>
|
2004-01-30 12:00:16 +00:00
|
|
|
<para>
|
|
|
|
The mixer interface is very audio-centric. However, with the software
|
|
|
|
flag set, the mixer can be used to mix any kind of stream in a N-to-1
|
|
|
|
element to join (not aggregate!) streams together into one output stream.
|
|
|
|
Conceptually, that's called mixing too. You can always use the element
|
|
|
|
factory's <quote>category</quote> to indicate type of your element. In
|
|
|
|
a software element that mixes random streams, you would not be required
|
|
|
|
to implement the <function>_get_volume ()</function> or
|
|
|
|
<function>_set_volume ()</function> functions. Rather, you would only
|
|
|
|
implement the <function>_set_record ()</function> to enable or disable
|
|
|
|
tracks in the output stream. to make sure that a mixer-implementing
|
|
|
|
element is of a certain type, check the element factory's category.
|
|
|
|
</para>
|
2004-01-28 09:07:11 +00:00
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-tuner" xreflabel="Tuner Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>Tuner Interface</title>
|
|
|
|
<para>
|
2004-01-30 12:00:16 +00:00
|
|
|
As opposed to the mixer interface, that's used to join together N streams
|
|
|
|
into one output stream by mixing all streams together, the tuner
|
|
|
|
interface is used in N-to-1 elements too, but instead of mixing the input
|
|
|
|
streams, it will select one stream and push the data of that stream to
|
|
|
|
the output stream. It will discard the data of all other streams. There
|
|
|
|
is a flag that indicates whether this is a software-tuner (in which case
|
|
|
|
it is a pure software implementation, with N sink pads and 1 source pad)
|
|
|
|
or a hardware-tuner, in which case it only has one source pad, and the
|
|
|
|
whole stream selection process is done in hardware. The software case can
|
|
|
|
be used in elements such as <emphasis>switch</emphasis>. The hardware
|
|
|
|
case can be used in elements with channel selection, such as video source
|
|
|
|
elements (v4lsrc, v4l2src, etc.). If you need a specific element type,
|
|
|
|
use the element factory's <quote>category</quote> to make sure that the
|
|
|
|
element is of the type that you need. Note that the interface itself is
|
|
|
|
highly analog-video-centric.
|
|
|
|
</para>
|
|
|
|
<para>
|
2004-07-27 15:01:10 +00:00
|
|
|
This interface requires the <ulink type="http"
|
|
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
|
|
GstImplemensInterface</classname></ulink>
|
2004-01-30 12:00:16 +00:00
|
|
|
interface to work correctly.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
The following example shows how to implement the tuner interface in an
|
|
|
|
element. It does not show the actual process of stream selection, that
|
|
|
|
is irrelevant for this section.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
#include <gst/tuner/tuner.h>
|
|
|
|
|
|
|
|
typedef struct _GstMyFilter {
|
|
|
|
[..]
|
|
|
|
gint active_input;
|
|
|
|
GList *channels;
|
|
|
|
} GstMyFilter;
|
|
|
|
|
|
|
|
static void gst_my_filter_implements_interface_init (GstImplementsInterfaceClass *iface);
|
|
|
|
static void gst_my_filter_tuner_interface_init (GstTunerClass *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 tuner_interface_info = {
|
|
|
|
(GInterfaceInitFunc) gst_my_filter_tuner_interface_init,
|
|
|
|
NULL,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
[..]
|
|
|
|
g_type_add_interface_static (my_filter_type,
|
|
|
|
GST_TYPE_IMPLEMENTS_INTERFACE,
|
|
|
|
&implements_interface_info);
|
|
|
|
g_type_add_interface_static (my_filter_type,
|
|
|
|
GST_TYPE_TUNER,
|
|
|
|
&tunerr_interface_info);
|
|
|
|
[..]
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_my_filter_init (GstMyFilter *filter)
|
|
|
|
{
|
|
|
|
GstTunerChannel *channel = NULL;
|
|
|
|
[..]
|
|
|
|
filter->active_input = 0;
|
|
|
|
filter->channels = NULL;
|
|
|
|
channel = g_object_new (GST_TYPE_TUNER_CHANNEL, NULL);
|
|
|
|
channel->label = g_strdup ("MyChannel");
|
|
|
|
channel->flags = GST_TUNER_CHANNEL_INPUT;
|
|
|
|
filter->channels = g_list_append (filter->channels, channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_my_filter_interface_supported (GstImplementsInterface *iface,
|
|
|
|
GType iface_type)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (iface_type == GST_TYPE_TUNER, FALSE);
|
|
|
|
|
|
|
|
/* for the sake of this example, we'll always support it. However, normally,
|
|
|
|
* you would check whether the device you've opened supports tuning. */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_my_filter_implements_interface_init (GstImplementsInterfaceClass *iface)
|
|
|
|
{
|
|
|
|
iface->supported = gst_my_filter_interface_supported;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const GList *
|
|
|
|
gst_my_filter_tuner_list_channels (GstTuner *tuner)
|
|
|
|
{
|
|
|
|
GstMyFilter *filter = GST_MY_FILTER (tuner);
|
|
|
|
|
|
|
|
return filter->channels;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstTunerChannel *
|
|
|
|
gst_my_filter_tuner_get_channel (GstTuner *tuner)
|
|
|
|
{
|
|
|
|
GstMyFilter *filter = GST_MY_FILTER (tuner);
|
|
|
|
|
|
|
|
return g_list_nth_data (filter->channels,
|
|
|
|
filter->active_input);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_my_filter_tuner_set_channel (GstTuner *tuner,
|
|
|
|
GstTunerChannel *channel)
|
|
|
|
{
|
|
|
|
GstMyFilter *filter = GST_MY_FILTER (tuner);
|
|
|
|
|
|
|
|
filter->active_input = g_list_index (filter->channels, channel);
|
|
|
|
g_assert (filter->active_input >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_my_filter_tuner_interface_init (GstTunerClass *iface)
|
|
|
|
{
|
|
|
|
iface->list_channels = gst_my_filter_tuner_list_channels;
|
|
|
|
iface->get_channel = gst_my_filter_tuner_get_channel;
|
|
|
|
iface->set_channel = gst_my_filter_tuner_set_channel;
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
As said, the tuner interface is very analog video-centric. It features
|
|
|
|
functions for selecting an input or output, and on inputs, it features
|
|
|
|
selection of a tuning frequency if the channel supports frequency-tuning
|
|
|
|
on that input. Likewise, it allows signal-strength-acquiring if the input
|
|
|
|
supports that. Frequency tuning can be used for radio or cable-TV tuning.
|
|
|
|
Signal-strength is an indication of the signal and can be used for
|
|
|
|
visual feedback to the user or for autodetection. Next to that, it also
|
|
|
|
features norm selection, which is only useful for analog video elements.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-colorbalance" xreflabel="Color Balance Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>Color Balance Interface</title>
|
|
|
|
<para>
|
|
|
|
WRITEME
|
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-propprobe" xreflabel="Property Probe Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>Property Probe Interface</title>
|
|
|
|
<para>
|
2004-01-28 14:16:59 +00:00
|
|
|
Property probing is a generic solution to the problem that properties'
|
|
|
|
value lists in an enumeration are static. We've shown enumerations in
|
2004-01-28 15:51:14 +00:00
|
|
|
<xref linkend="chapter-building-args"/>. Property probing tries to accomplish
|
2004-01-28 14:16:59 +00:00
|
|
|
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
|
2005-05-17 17:22:37 +00:00
|
|
|
of an element. The contents of an enumeration list are static. Currently,
|
2004-01-28 14:16:59 +00:00
|
|
|
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.
|
2004-07-27 15:01:10 +00:00
|
|
|
<symbol>NULL</symbol> is a valid return value, too. The process of
|
2004-01-28 14:16:59 +00:00
|
|
|
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
|
2004-07-27 15:01:10 +00:00
|
|
|
<!-- FIXME: add link, but this is in ./gst-plugins/gst-libs/gst/propertyprobe/propertyprobe.c-->
|
2004-01-28 14:16:59 +00:00
|
|
|
<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.
|
2004-07-27 15:01:10 +00:00
|
|
|
Indeed, this value is a <type>gboolean</type> so it doesn't
|
2004-01-28 14:16:59 +00:00
|
|
|
make much sense. Then again, it's only an example.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
#include <gst/propertyprobe/propertyprobe.h>
|
|
|
|
|
|
|
|
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,
|
|
|
|
&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 (&value, G_TYPE_BOOLEAN);
|
|
|
|
|
|
|
|
/* add TRUE */
|
|
|
|
g_value_set_boolean (&value, TRUE);
|
|
|
|
g_value_array_append (array, &value);
|
|
|
|
|
|
|
|
/* add FALSE */
|
|
|
|
g_value_set_boolean (&value, FALSE);
|
|
|
|
g_value_array_append (array, &value);
|
|
|
|
|
|
|
|
g_value_unset (&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.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-xoverlay" xreflabel="X Overlay Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>X Overlay Interface</title>
|
|
|
|
<para>
|
2004-01-28 17:45:47 +00:00
|
|
|
An X Overlay is basically a video output in a XFree86 drawable. Elements
|
|
|
|
implementing this interface will draw video in a X11 window. Through this
|
|
|
|
interface, applications will be proposed 2 different modes to work with
|
|
|
|
a plugin implemeting it. The first mode is a passive mode where the plugin
|
|
|
|
owns, creates and destroys the X11 window. The second mode is an active
|
|
|
|
mode where the application handles the X11 window creation and then tell
|
|
|
|
the plugin where it should output video. Let's get a bit deeper in those
|
|
|
|
modes...
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
A plugin drawing video output in a X11 window will need to have that
|
|
|
|
window at one stage or another. Passive mode simply means that no window
|
|
|
|
has been given to the plugin before that stage, so the plugin created the
|
|
|
|
window by itself. In that case the plugin is responsible of destroying
|
|
|
|
that window when it's not needed anymore and it has to tell the
|
|
|
|
applications that a window has been created so that the application can
|
|
|
|
use it. This is done using the <classname>have_xwindow_id</classname>
|
|
|
|
signal that can be emitted from the plugin with the
|
|
|
|
<function>gst_x_overlay_got_xwindow_id</function> method.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
As you probably guessed already active mode just means sending a X11
|
|
|
|
window to the plugin so that video output goes there. This is done using
|
|
|
|
the <function>gst_x_overlay_set_xwindow_id</function> method.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
It is possible to switch from one mode to another at any moment, so the
|
|
|
|
plugin implementing this interface has to handle all cases. There are only
|
|
|
|
2 methods that plugins writers have to implement and they most probably
|
|
|
|
look like that :
|
|
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
static void
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_my_filter_set_xwindow_id (GstXOverlay *overlay, XID xwindow_id)
|
2004-01-28 17:45:47 +00:00
|
|
|
{
|
2004-01-29 12:46:19 +00:00
|
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
|
|
|
|
|
|
|
if (my_filter->window)
|
|
|
|
gst_my_filter_destroy_window (my_filter->window);
|
2004-01-28 17:45:47 +00:00
|
|
|
|
2004-01-29 12:46:19 +00:00
|
|
|
my_filter->window = xwindow_id;
|
2004-01-28 17:45:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_my_filter_get_desired_size (GstXOverlay *overlay,
|
|
|
|
guint *width, guint *height)
|
2004-01-28 17:45:47 +00:00
|
|
|
{
|
2004-01-29 12:46:19 +00:00
|
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
2004-01-28 17:45:47 +00:00
|
|
|
|
2004-01-29 12:46:19 +00:00
|
|
|
*width = my_filter->width;
|
|
|
|
*height = my_filter->height;
|
2004-01-28 17:45:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_my_filter_xoverlay_init (GstXOverlayClass *iface)
|
2004-01-28 17:45:47 +00:00
|
|
|
{
|
2004-01-29 12:46:19 +00:00
|
|
|
iface->set_xwindow_id = gst_my_filter_set_xwindow_id;
|
|
|
|
iface->get_desired_size = gst_my_filter_get_desired_size;
|
2004-01-28 17:45:47 +00:00
|
|
|
}
|
|
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
|
|
You will also need to use the interface methods to fire signals when
|
|
|
|
needed such as in the pad link function where you will know the video
|
|
|
|
geometry and maybe create the window.
|
2004-01-28 09:07:11 +00:00
|
|
|
</para>
|
2004-01-28 17:45:47 +00:00
|
|
|
<programlisting><![CDATA[
|
2004-01-29 12:46:19 +00:00
|
|
|
static MyFilterWindow *
|
|
|
|
gst_my_filter_window_create (GstMyFilter *my_filter, gint width, gint height)
|
2004-01-28 17:45:47 +00:00
|
|
|
{
|
2004-01-29 12:46:19 +00:00
|
|
|
MyFilterWindow *window = g_new (MyFilterWindow, 1);
|
2004-01-28 17:45:47 +00:00
|
|
|
...
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (my_filter), window->win);
|
2004-01-28 17:45:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static GstPadLinkReturn
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_my_filter_sink_link (GstPad *pad, const GstCaps *caps)
|
2004-01-28 17:45:47 +00:00
|
|
|
{
|
2004-01-29 12:46:19 +00:00
|
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
2004-01-28 17:45:47 +00:00
|
|
|
gint width, height;
|
|
|
|
gboolean ret;
|
|
|
|
...
|
|
|
|
ret = gst_structure_get_int (structure, "width", &width);
|
|
|
|
ret &= gst_structure_get_int (structure, "height", &height);
|
|
|
|
if (!ret) return GST_PAD_LINK_REFUSED;
|
|
|
|
|
2004-01-29 12:46:19 +00:00
|
|
|
if (!my_filter->window)
|
|
|
|
my_filter->window = gst_my_filter_create_window (my_filter, width, height);
|
2004-01-28 17:45:47 +00:00
|
|
|
|
2004-01-29 12:46:19 +00:00
|
|
|
gst_x_overlay_got_desired_size (GST_X_OVERLAY (my_filter),
|
2004-01-28 17:45:47 +00:00
|
|
|
width, height);
|
|
|
|
...
|
|
|
|
}
|
|
|
|
]]></programlisting>
|
2004-01-28 09:07:11 +00:00
|
|
|
</sect1>
|
|
|
|
|
2004-01-28 15:51:14 +00:00
|
|
|
<sect1 id="section-iface-navigation" xreflabel="Navigation Interface">
|
2004-01-28 09:07:11 +00:00
|
|
|
<title>Navigation Interface</title>
|
|
|
|
<para>
|
|
|
|
WRITEME
|
|
|
|
</para>
|
|
|
|
</sect1>
|
2004-01-27 14:58:21 +00:00
|
|
|
</chapter>
|