mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
626cbdc8b2
Original commit message from CVS: cleanup of unused and forgoten sections fixed links from the manual and the pwg to the API docs added more notes to README
724 lines
26 KiB
XML
724 lines
26 KiB
XML
<chapter id="chapter-advanced-interfaces">
|
|
<title>Interfaces</title>
|
|
<para>
|
|
Previously, in the chapter <xref linkend="chapter-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.
|
|
</para>
|
|
<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 <ulink type="http"
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
GstImplementsInterface</classname></ulink>.
|
|
</para>
|
|
<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>
|
|
|
|
<sect1 id="section-iface-general" xreflabel="How to Implement Interfaces">
|
|
<title>How to Implement Interfaces</title>
|
|
<para>
|
|
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 <ulink type="http"
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
GstImplementsInterface</classname></ulink>. 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 <ulink type="http" url="../../gstreamer/html/GstImplementsInterface.html">
|
|
<classname>GstImplementsInterface</classname></ulink>, see the next section
|
|
about the mixer interface: <xref linkend="section-iface-mixer"/>.
|
|
</para>
|
|
<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>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-mixer" xreflabel="Mixer Interface">
|
|
<title>Mixer Interface</title>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
The mixer interface requires the <ulink type="http"
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
GstImplementsInterface</classname></ulink>
|
|
interface to be implemented by the element. The example below will
|
|
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
|
|
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>
|
|
<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>
|
|
<programlisting>
|
|
#include <gst/mixer/mixer.h>
|
|
|
|
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>
|
|
<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>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-tuner" xreflabel="Tuner Interface">
|
|
<title>Tuner Interface</title>
|
|
<para>
|
|
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>
|
|
This interface requires the <ulink type="http"
|
|
url="../../gstreamer/html/GstImplementsInterface.html"><classname>
|
|
GstImplemensInterface</classname></ulink>
|
|
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.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-colorbalance" xreflabel="Color Balance Interface">
|
|
<title>Color Balance Interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-propprobe" xreflabel="Property Probe Interface">
|
|
<title>Property Probe Interface</title>
|
|
<para>
|
|
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="chapter-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.
|
|
<symbol>NULL</symbol> 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
|
|
<!-- FIXME: add link, but this is in ./gst-plugins/gst-libs/gst/propertyprobe/propertyprobe.c-->
|
|
<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 <type>gboolean</type> so it doesn't
|
|
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.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-profile" xreflabel="Profile Interface">
|
|
<title>Profile Interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-xoverlay" xreflabel="X Overlay Interface">
|
|
<title>X Overlay Interface</title>
|
|
<para>
|
|
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
|
|
gst_my_filter_set_xwindow_id (GstXOverlay *overlay, XID xwindow_id)
|
|
{
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
|
|
|
if (my_filter->window)
|
|
gst_my_filter_destroy_window (my_filter->window);
|
|
|
|
my_filter->window = xwindow_id;
|
|
}
|
|
|
|
static void
|
|
gst_my_filter_get_desired_size (GstXOverlay *overlay,
|
|
guint *width, guint *height)
|
|
{
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
|
|
|
*width = my_filter->width;
|
|
*height = my_filter->height;
|
|
}
|
|
|
|
static void
|
|
gst_my_filter_xoverlay_init (GstXOverlayClass *iface)
|
|
{
|
|
iface->set_xwindow_id = gst_my_filter_set_xwindow_id;
|
|
iface->get_desired_size = gst_my_filter_get_desired_size;
|
|
}
|
|
]]></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.
|
|
</para>
|
|
<programlisting><![CDATA[
|
|
static MyFilterWindow *
|
|
gst_my_filter_window_create (GstMyFilter *my_filter, gint width, gint height)
|
|
{
|
|
MyFilterWindow *window = g_new (MyFilterWindow, 1);
|
|
...
|
|
gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (my_filter), window->win);
|
|
}
|
|
|
|
static GstPadLinkReturn
|
|
gst_my_filter_sink_link (GstPad *pad, const GstCaps *caps)
|
|
{
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
|
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;
|
|
|
|
if (!my_filter->window)
|
|
my_filter->window = gst_my_filter_create_window (my_filter, width, height);
|
|
|
|
gst_x_overlay_got_desired_size (GST_X_OVERLAY (my_filter),
|
|
width, height);
|
|
...
|
|
}
|
|
]]></programlisting>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-navigation" xreflabel="Navigation Interface">
|
|
<title>Navigation Interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
</chapter>
|