mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
237 lines
8.9 KiB
XML
237 lines
8.9 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 very powerful, but it has two big disadvantages:
|
|
first of all, it is too generic, and second, it isn't dynamic.
|
|
</para>
|
|
<para>
|
|
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 <classname>GParamSpec</classname> type. Another problem,
|
|
is that things like parameter grouping, function grouping, or parameter
|
|
coupling are not
|
|
really possible.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</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.
|
|
</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. First of all,
|
|
properties can be more easily introspected. Second, 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 initiated 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.
|
|
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.
|
|
</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),
|
|
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 */
|
|
}
|
|
</programlisting>
|
|
<para>
|
|
Or more conveniently:
|
|
</para>
|
|
<programlisting>
|
|
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));
|
|
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-uri" xreflabel="URI interface">
|
|
<title>URI interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-colorbalance" xreflabel="Color Balance Interface">
|
|
<title>Color Balance Interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-xoverlay" xreflabel="Video Overlay Interface">
|
|
<title>Video Overlay Interface</title>
|
|
<para>
|
|
The #GstVideoOverlay interface is used for 2 main purposes :
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
To get a grab on the Window where the video sink element is going to render.
|
|
This is achieved by either being informed about the Window identifier that
|
|
the video sink element generated, or by forcing the video sink element to use
|
|
a specific Window identifier for rendering.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
To force a redrawing of the latest video frame the video sink element
|
|
displayed on the Window. Indeed if the #GstPipeline is in #GST_STATE_PAUSED
|
|
state, moving the Window around will damage its content. Application
|
|
developers will want to handle the Expose events themselves and force the
|
|
video sink element to refresh the Window's content.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
<para>
|
|
A plugin drawing video output in a video 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 any more 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-window-handle</classname>
|
|
message that can be posted from the plugin with the
|
|
<function>gst_video_overlay_got_window_handle</function> method.
|
|
</para>
|
|
<para>
|
|
As you probably guessed already active mode just means sending a video
|
|
window to the plugin so that video output goes there. This is done using
|
|
the <function>gst_video_overlay_set_window_handle</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_window_handle (GstVideoOverlay *overlay, guintptr handle)
|
|
{
|
|
GstMyFilter *my_filter = GST_MY_FILTER (overlay);
|
|
|
|
if (my_filter->window)
|
|
gst_my_filter_destroy_window (my_filter->window);
|
|
|
|
my_filter->window = handle;
|
|
}
|
|
|
|
static void
|
|
gst_my_filter_xoverlay_init (GstVideoOverlayClass *iface)
|
|
{
|
|
iface->set_window_handle = gst_my_filter_set_window_handle;
|
|
}
|
|
]]></programlisting>
|
|
<para>
|
|
You will also need to use the interface methods to post messages when
|
|
needed such as when receiving a CAPS event 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_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (my_filter), window->win);
|
|
}
|
|
|
|
/* called from the event handler for CAPS events */
|
|
static gboolean
|
|
gst_my_filter_sink_set_caps (GstMyFilter *my_filter, GstCaps *caps)
|
|
{
|
|
gint width, height;
|
|
gboolean ret;
|
|
...
|
|
ret = gst_structure_get_int (structure, "width", &width);
|
|
ret &= gst_structure_get_int (structure, "height", &height);
|
|
if (!ret) return FALSE;
|
|
|
|
gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (my_filter));
|
|
|
|
if (!my_filter->window)
|
|
my_filter->window = gst_my_filter_create_window (my_filter, width, height);
|
|
|
|
...
|
|
}
|
|
]]></programlisting>
|
|
</sect1>
|
|
|
|
<sect1 id="section-iface-navigation" xreflabel="Navigation Interface">
|
|
<title>Navigation Interface</title>
|
|
<para>
|
|
WRITEME
|
|
</para>
|
|
</sect1>
|
|
</chapter>
|