| <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. 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. First of all, |
| properties |
| can be saved in XML files. 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 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-uri" xreflabel="URI interface"> |
| <title>URI interface</title> |
| <para> |
| WRITEME |
| </para> |
| </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 an enumeration list are static. Currently, |
| 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-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> |