mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
Add a chapter on caps negotiation, simplify the original code samples a bit w.r.t. caps negotiation, add link to the ...
Original commit message from CVS: * docs/pwg/advanced-negotiation.xml: * docs/pwg/building-boiler.xml: * docs/pwg/building-pads.xml: * docs/pwg/pwg.xml: * examples/pwg/Makefile.am: Add a chapter on caps negotiation, simplify the original code samples a bit w.r.t. caps negotiation, add link to the advanced section. Add a bunch of examples showing different use cases of different types of caps negotiation. Upstream renegotiation isn't fully documented yet since nobody knows how that works.
This commit is contained in:
parent
2953b3174e
commit
a13be0a71e
7 changed files with 526 additions and 87 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
||||||
|
2005-07-06 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
|
* docs/pwg/advanced-negotiation.xml:
|
||||||
|
* docs/pwg/building-boiler.xml:
|
||||||
|
* docs/pwg/building-pads.xml:
|
||||||
|
* docs/pwg/pwg.xml:
|
||||||
|
* examples/pwg/Makefile.am:
|
||||||
|
Add a chapter on caps negotiation, simplify the original code
|
||||||
|
samples a bit w.r.t. caps negotiation, add link to the advanced
|
||||||
|
section. Add a bunch of examples showing different use cases of
|
||||||
|
different types of caps negotiation. Upstream renegotiation isn't
|
||||||
|
fully documented yet since nobody knows how that works.
|
||||||
|
|
||||||
2005-07-06 Thomas Vander Stichele <thomas at apestaart dot org>
|
2005-07-06 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||||
|
|
||||||
* check/gst/gstpad.c:
|
* check/gst/gstpad.c:
|
||||||
|
|
439
docs/pwg/advanced-negotiation.xml
Normal file
439
docs/pwg/advanced-negotiation.xml
Normal file
|
@ -0,0 +1,439 @@
|
||||||
|
<chapter id="chapter-negotiation" xreflabel="Caps negotiation">
|
||||||
|
<title>Caps negotiation</title>
|
||||||
|
<para>
|
||||||
|
Caps negotiation is the process where elements configure themselves
|
||||||
|
and each other for streaming a particular media format over their pads.
|
||||||
|
Since different types of elements have different requirements for the
|
||||||
|
media formats they can negotiate to, it is important that this process
|
||||||
|
is generic and implements all those use cases correctly.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In this chapter, we will discuss downstream negotiation and upstream
|
||||||
|
negotiation from a pipeline perspective, implicating the responsibilities
|
||||||
|
of different types of elements in a pipeline, and we will introduce the
|
||||||
|
concept of <emphasis>fixed caps</emphasis>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="section-nego-requirements" xreflabel="Caps negotiation use cases">
|
||||||
|
<title>Caps negotiation use cases</title>
|
||||||
|
<para>
|
||||||
|
Let's take the case of a file source, linked to a demuxer, linked to a
|
||||||
|
decoder, linked to a converter with a caps filter and finally an audio
|
||||||
|
output. When dataflow originally starts, the demuxer will parse the
|
||||||
|
file header (e.g. the Ogg headers), and notice that there is, for
|
||||||
|
example, a Vorbis stream in this Ogg file. Noticing that, it will
|
||||||
|
create an output pad for the Vorbis elementary stream and set a
|
||||||
|
Vorbis-caps on it. Lastly, it adds the pad. As of this point, the pad
|
||||||
|
is ready to be used to stream data, and so the Ogg demuxer is now done.
|
||||||
|
This pad is <emphasis>not</emphasis> re-negotiatable, since the type of
|
||||||
|
the data stream is embedded within the data.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The Vorbis decoder will decode the Vorbis headers and the Vorbis data
|
||||||
|
coming in on its sinkpad. Now, some decoders may be able to output in
|
||||||
|
multiple output formats, for example both 16-bit integer output and
|
||||||
|
floating-point output, whereas other decoders may be able to only decode
|
||||||
|
into one specific format, e.g. only floating-point (32-bit) audio. Those
|
||||||
|
two cases have consequences for how caps negotiation should be
|
||||||
|
implemented in this decoder element. In the one case, it is possible to
|
||||||
|
use fixed caps, and you're done. In the other case, however, you should
|
||||||
|
implement the possibility for <emphasis>renegotiation</emphasis> in this
|
||||||
|
element, which is the possibility for the data format to be changed to
|
||||||
|
another format at some point in the future. We will discuss how to do
|
||||||
|
this in one of the sections further on in this chapter.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The filter can be used by applications to force, for example, a specific
|
||||||
|
channel configuration (5.1/surround or 2.0/stereo), on the pipeline, so
|
||||||
|
that the user can enjoy sound coming from all its speakers. The audio
|
||||||
|
sink, in this example, is a standard ALSA output element (alsasink).
|
||||||
|
The converter element supports any-to-any, and the filter will make sure
|
||||||
|
that only a specifically wanted channel configuration streams through
|
||||||
|
this link (as provided by the user's channel configuration preference).
|
||||||
|
By changing this preference while the pipeline is running, some elements
|
||||||
|
will have to renegotiate <emphasis>while the pipeline is
|
||||||
|
running</emphasis>. This is done through upstream caps renegotiation.
|
||||||
|
That, too, will be discussed in detail in a section further below.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In order for caps negotiation on non-fixed links to work correctly,
|
||||||
|
pads can optionally implement a function that tells peer elements what
|
||||||
|
formats it supports and/or preferes. When upstream renegotiation is
|
||||||
|
triggered, this becomes important.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Downstream elements are notified of a newly set caps only when data
|
||||||
|
is actually passing their pad. This is because caps is attached to
|
||||||
|
buffers during dataflow. So when the vorbis decoder sets a caps on
|
||||||
|
its source pad (to configure the output format), the converter will
|
||||||
|
not yet be notified. Instead, the converter will only be notified
|
||||||
|
when the decoder pushes a buffer over its source pad to the converter.
|
||||||
|
Right before calling the chain-function in the converter, &GStreamer;
|
||||||
|
will check whether the format that was previously negotiated still
|
||||||
|
applies to this buffer. If not, it first calls the setcaps-function
|
||||||
|
of the converter to configure it for the new format. Only after that
|
||||||
|
will it call the chain function of the converter.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-nego-fixedcaps" xreflabel="Fixed caps">
|
||||||
|
<title>Fixed caps</title>
|
||||||
|
<para>
|
||||||
|
The simplest way in which to do caps negotiation is setting a fixed
|
||||||
|
caps on a pad. After a fixed caps has been set, the pad can not be
|
||||||
|
renegotiated from the outside. The only way to reconfigure the pad
|
||||||
|
is for the element owning the pad to set a new fixed caps on the pad.
|
||||||
|
Fixed caps is a setup property for pads, called when creating the pad:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
[..]
|
||||||
|
pad = gst_pad_new_from_template (..);
|
||||||
|
gst_pad_use_fixed_caps (pad);
|
||||||
|
[..]
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
The fixed caps can then be set on the pad by calling
|
||||||
|
<function>gst_pad_set_caps ()</function>.
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
[..]
|
||||||
|
caps = gst_caps_new_simple ("audio/x-raw-float",
|
||||||
|
"width", G_TYPE_INT, 32,
|
||||||
|
"endianness", G_TYPE_INT, G_BYTE_ORDER,
|
||||||
|
"buffer-frames", G_TYPE_INT, <bytes-per-frame>,
|
||||||
|
"rate", G_TYPE_INT, <samplerate>,
|
||||||
|
"channels", G_TYPE_INT, <num-channels>, NULL);
|
||||||
|
if (!gst_pad_set_caps (pad, caps)) {
|
||||||
|
GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
|
||||||
|
("Some debug information here"));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
[..]
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
Elements that could implement fixed caps (on their source pads) are,
|
||||||
|
in general, all elements that are not renegotiatable. Examples include:
|
||||||
|
</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A typefinder, since the type found is part of the actual data stream
|
||||||
|
and can thus not be re-negotiated.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Pretty much all demuxers, since the contained elementary data
|
||||||
|
streams are defined in the file headers, and thus not
|
||||||
|
renegotiatable.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Some decoders, where the format is embedded in the datastream
|
||||||
|
and not part of the peercaps <emphasis>and</emphasis> where the
|
||||||
|
decoder itself is not reconfigureable, too.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
<para>
|
||||||
|
All other elements that need to be configured for the format should
|
||||||
|
implement full caps negotiation, which will be explained in the next
|
||||||
|
few sections.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-nego-downstream" xreflabel="Downstream caps negotiation">
|
||||||
|
<title>Downstream caps negotiation</title>
|
||||||
|
<para>
|
||||||
|
Downstream negotiation takes place when a format needs to be set on a
|
||||||
|
source pad to configure the output format, but this element allows
|
||||||
|
renegotiation because its format is configured on the sinkpad caps,
|
||||||
|
or because it supports multiple formats. The requirements for doing
|
||||||
|
the actual negotiation differ slightly.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="section-nego-downstream-embed"
|
||||||
|
xreflabel="Negotiating caps embedded in input caps">
|
||||||
|
<title>Negotiating caps embedded in input caps</title>
|
||||||
|
<para>
|
||||||
|
Many elements, particularly effects and converters, will be able
|
||||||
|
to parse the format of the stream from their input caps, and decide
|
||||||
|
the output format right at that time already. When renegotiation
|
||||||
|
takes place, some may merely need to "forward" the renegotiation
|
||||||
|
backwards upstream (more on that later). For those elements, all
|
||||||
|
(downstream) caps negotiation can be done in something that we
|
||||||
|
call the <function>_setcaps ()</function> function. This function is
|
||||||
|
called when a buffer is pushed over a pad, but the format on this
|
||||||
|
buffer is not the same as the format that was previously negotiated
|
||||||
|
(or, similarly, no format was negotiated yet so far).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In the <function>_setcaps ()</function>-function, the element can
|
||||||
|
forward the caps to the next element and, if that pad accepts the
|
||||||
|
format too, the element can parse the relevant parameters from the
|
||||||
|
caps and configure itself internally. The caps passed to this function
|
||||||
|
is <emphasis>always</emphasis> a subset of the template caps, so
|
||||||
|
there's no need for extensive safety checking. The following example
|
||||||
|
should give a clear indication of how such a function can be
|
||||||
|
implemented:
|
||||||
|
</para>
|
||||||
|
<programlisting><!-- example-begin forwardcaps.c a --><!--
|
||||||
|
#include "init.func"
|
||||||
|
static GstCaps *
|
||||||
|
gst_my_filter_getcaps (GstPad * pad)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
--><!-- example-end forwardcaps.c a -->
|
||||||
|
<!-- example-begin forwardcaps.c b -->
|
||||||
|
static gboolean
|
||||||
|
gst_my_filter_setcaps (GstPad *pad,
|
||||||
|
GstCaps *caps)
|
||||||
|
{
|
||||||
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
|
GstStructure *s;
|
||||||
|
|
||||||
|
/* forward-negotiate */
|
||||||
|
if (!gst_pad_set_caps (filter->srcpad, caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* negotiation succeeded, so now configure ourselves */
|
||||||
|
s = gst_caps_get_structure (caps, 0);
|
||||||
|
gst_structure_get_int (s, "rate", &filter->samplerate);
|
||||||
|
gst_structure_get_int (s, "channels", &filter->channels);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
<!-- example-end forwardcaps.c b -->
|
||||||
|
<!-- example-begin forwardcaps.c c --><!--
|
||||||
|
#include "chain.func"
|
||||||
|
#include "state.func"
|
||||||
|
#include "register.func"
|
||||||
|
--><!-- example-end forwardcaps.c c --></programlisting>
|
||||||
|
<para>
|
||||||
|
There may also be cases where the filter actually is able to
|
||||||
|
<emphasis>change</emphasis> the format of the stream. In those cases,
|
||||||
|
it will negotiate a new format. Obviously, the element should first
|
||||||
|
attempt to configure <quote>pass-through</quote>, which means that
|
||||||
|
it does not change the stream's format. However, if that fails,
|
||||||
|
then it should call <function>gst_pad_get_allowed_caps ()</function>
|
||||||
|
on its sourcepad to get a list of supported formats on the outputs,
|
||||||
|
and pick the first. The return value of that function is guaranteed
|
||||||
|
to be a subset of the template caps.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Let's look at the example of an element that can convert between
|
||||||
|
samplerates, so where input and output samplerate don't have to be
|
||||||
|
the same:
|
||||||
|
</para>
|
||||||
|
<programlisting><!-- example-begin convertcaps.c a --><!--
|
||||||
|
#include "init.func"
|
||||||
|
static GstCaps *
|
||||||
|
gst_my_filter_getcaps (GstPad * pad)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static GstBuffer *
|
||||||
|
gst_my_filter_convert (GstMyFilter *filter, GstBuffer *in)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static gboolean
|
||||||
|
gst_my_filter_event (GstPad * pad, GstEvent * event)
|
||||||
|
{
|
||||||
|
return gst_pad_event_default (pad, event);
|
||||||
|
}
|
||||||
|
--><!-- example-end convertcaps.c a -->
|
||||||
|
<!-- example-begin convertcaps.c b -->
|
||||||
|
static gboolean
|
||||||
|
gst_my_filter_setcaps (GstPad *pad,
|
||||||
|
GstCaps *caps)
|
||||||
|
{
|
||||||
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
|
|
||||||
|
if (gst_pad_set_caps (filter->sinkpad, caps)) {
|
||||||
|
filter->passthrough = TRUE;
|
||||||
|
} else {
|
||||||
|
GstCaps *othercaps, *newcaps;
|
||||||
|
GstStructure *s = gst_caps_get_structure (caps, 0), *others;
|
||||||
|
|
||||||
|
/* no passthrough, setup internal conversion */
|
||||||
|
gst_structure_get_int (s, "channels", &filter->channels);
|
||||||
|
othercaps = gst_pad_get_allowed_caps (filter->srcpad);
|
||||||
|
others = gst_caps_get_structure (othercaps, 0);
|
||||||
|
gst_structure_set (others,
|
||||||
|
"channels", G_TYPE_INT, filter->channels, NULL);
|
||||||
|
|
||||||
|
/* now, the samplerate value can optionally have multiple values, so
|
||||||
|
* we "fixate" it, which means that one fixed value is chosen */
|
||||||
|
newcaps = gst_caps_copy_nth (othercaps, 0);
|
||||||
|
gst_caps_unref (othercaps);
|
||||||
|
gst_pad_fixate_caps (filter->srcpad, newcaps);
|
||||||
|
if (!gst_pad_set_caps (filter->srcpad, newcaps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* we are now set up, configure internally */
|
||||||
|
filter->passthrough = FALSE;
|
||||||
|
gst_structure_get_int (s, "rate", &filter->from_samplerate);
|
||||||
|
others = gst_caps_get_structure (newcaps, 0);
|
||||||
|
gst_structure_get_int (others, "rate", &filter->to_samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_my_filter_chain (GstPad *pad,
|
||||||
|
GstBuffer *buf)
|
||||||
|
{
|
||||||
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
|
GstBuffer *out;
|
||||||
|
|
||||||
|
/* push on if in passthrough mode */
|
||||||
|
if (filter->passthrough)
|
||||||
|
return gst_pad_push (filter->srcpad, buf);
|
||||||
|
|
||||||
|
/* convert, push */
|
||||||
|
out = gst_my_filter_convert (filter, buf);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
|
||||||
|
return gst_pad_push (filter->srcpad, out);
|
||||||
|
}
|
||||||
|
<!-- example-end convertcaps.c b -->
|
||||||
|
<!-- example-begin convertcaps.c c --><!--
|
||||||
|
#include "state.func"
|
||||||
|
#include "register.func"
|
||||||
|
--><!-- example-end convertcaps.c c --></programlisting>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="section-nego-downstream-parse"
|
||||||
|
xreflabel="Parsing and setting caps">
|
||||||
|
<title>Parsing and setting caps</title>
|
||||||
|
<para>
|
||||||
|
Other elements, such as certain types of decoders, will not be able
|
||||||
|
to parse the caps from their input, simply because the input format
|
||||||
|
does not contain the information required to know the output format
|
||||||
|
yet; rather, the data headers need to be parsed, too. In many cases,
|
||||||
|
fixed-caps will be enough, but in some cases, particularly in cases
|
||||||
|
where such decoders are renegotiatable, it is also possible to use
|
||||||
|
full caps negotiation.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Fortunately, the code required to do so is very similar to the last
|
||||||
|
code example in <xref linkend="section-nego-downstream-embed"/>, with
|
||||||
|
the difference being that the caps is selected in the <function>_chain
|
||||||
|
()</function>-function rather than in the <function>_setcaps
|
||||||
|
()</function>-function. The rest, as for getting all allowed caps from
|
||||||
|
the source pad, fixating and such, is all the same. Re-negotiation,
|
||||||
|
which will be handled in the next section, is very different for such
|
||||||
|
elements, though.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-nego-upstream" xreflabel="Upstream caps (re)negotiation">
|
||||||
|
<title>Upstream caps (re)negotiation</title>
|
||||||
|
<para>
|
||||||
|
Upstream negotiation's primary use is to renegotiate (part of) an
|
||||||
|
already-negotiated pipeline to a new format. Some practical examples
|
||||||
|
include to select a different video size because the size of the video
|
||||||
|
window changed, and the video output itself is not capable of rescaling,
|
||||||
|
or because the audio channel configuration changed.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Upstream caps renegotiation is done in the <function>gst_pad_alloc_buffer
|
||||||
|
()</function>-function. The idea here is that an element requesting a
|
||||||
|
buffer from downstream, has to specify the type of that buffer. If
|
||||||
|
renegotiation is to take place, this type will no longer apply, and the
|
||||||
|
downstream element will set a new caps on the provided buffer. Next,
|
||||||
|
&GStreamer; will trigger renegotiation on the sourcepad of the element
|
||||||
|
before the function returns.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
It is important to note here that different elements actually have
|
||||||
|
different responsibilities here:
|
||||||
|
</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Elements should implement a <quote>padalloc</quote>-function in
|
||||||
|
order to be able to change format on renegotiation. This is also
|
||||||
|
true for filters and converters.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Elements should allocate new buffers using
|
||||||
|
<function>gst_pad_alloc_buffer ()</function>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Elements that are renegotiatable should implement a
|
||||||
|
<quote>setcaps</quote>-function on their sourcepad as well.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
<para>
|
||||||
|
Unfortunately, not all details here have been worked out yet, so this
|
||||||
|
documentation is incomplete. FIXME.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-nego-getcaps" xreflabel="Implementing a getcaps function">
|
||||||
|
<title>Implementing a getcaps function</title>
|
||||||
|
<para>
|
||||||
|
A <function>_getcaps ()</function>-function is called when a peer
|
||||||
|
element would like to know which formats this element supports, and
|
||||||
|
in what order of preference. The return value should be all formats
|
||||||
|
that this elements supports, taking into account limitations of peer
|
||||||
|
elements further downstream or upstream, sorted by order of preference,
|
||||||
|
highest preference first.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
</para>
|
||||||
|
<programlisting><!-- example-begin getcaps.c a --><!--
|
||||||
|
#include "init.func"
|
||||||
|
--><!-- example-end getcaps.c a -->
|
||||||
|
<!-- example-begin getcaps.c b -->
|
||||||
|
static GstCaps *
|
||||||
|
gst_my_filter_getcaps (GstPad *pad)
|
||||||
|
{
|
||||||
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
|
GstPad *otherpad = (pad == filter->srcpad) ? filter->sinkpad :
|
||||||
|
filter->srcpad;
|
||||||
|
GstCaps *othercaps = gst_pad_get_allowed_caps (otherpad), *caps;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
/* We support *any* samplerate, indifferent from the samplerate
|
||||||
|
* supported by the linked elements on both sides. */
|
||||||
|
for (i = 0; i < gst_caps_get_size (othercaps); i++) {
|
||||||
|
GstStructure *structure = gst_caps_get_structure (othercaps, i);
|
||||||
|
|
||||||
|
gst_structure_remove_field (structure, "rate");
|
||||||
|
}
|
||||||
|
caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
|
||||||
|
gst_caps_unref (othercaps);
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
<!-- example-end getcaps.c b -->
|
||||||
|
<!-- example-begin getcaps.c c --><!--
|
||||||
|
static gboolean
|
||||||
|
gst_my_filter_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
#include "chain.func"
|
||||||
|
#include "state.func"
|
||||||
|
#include "register.func"
|
||||||
|
--><!-- example-end getcaps.c c --></programlisting>
|
||||||
|
<para>
|
||||||
|
Using all the knowledge you've acquired by reading this chapter, you
|
||||||
|
should be able to write an element that does correct caps negotiation.
|
||||||
|
If in doubt, look at other elements of the same type in our CVS
|
||||||
|
repository to get an idea of how they do what you want to do.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
|
@ -147,6 +147,8 @@ typedef struct _GstMyFilter {
|
||||||
<!-- example-end filter.h a -->
|
<!-- example-end filter.h a -->
|
||||||
<!-- example-begin filter.h b --><!--
|
<!-- example-begin filter.h b --><!--
|
||||||
gint samplerate, channels;
|
gint samplerate, channels;
|
||||||
|
gint from_samplerate, to_samplerate;
|
||||||
|
gboolean passthrough;
|
||||||
--><!-- example-end filter.h b -->
|
--><!-- example-end filter.h b -->
|
||||||
<!-- example-begin filter.h c -->
|
<!-- example-begin filter.h c -->
|
||||||
} GstMyFilter;
|
} GstMyFilter;
|
||||||
|
|
|
@ -8,23 +8,20 @@
|
||||||
of your element, and that makes them a very important item in the process
|
of your element, and that makes them a very important item in the process
|
||||||
of element creation. In the boilerplate code, we have seen how static pad
|
of element creation. In the boilerplate code, we have seen how static pad
|
||||||
templates take care of registering pad templates with the element class.
|
templates take care of registering pad templates with the element class.
|
||||||
Here, we will see how to create actual elements, use <function>_link ()</function>
|
Here, we will see how to create actual elements, use a <function>_setcaps
|
||||||
and <function>_getcaps ()</function> functions to let other elements know
|
()</function>-functions to configure for a particular format and how to
|
||||||
their capabilities and how to register functions to let data flow through
|
register functions to let data flow through the element.
|
||||||
the element.
|
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
In the element <function>_init ()</function> function, you create the pad
|
In the element <function>_init ()</function> function, you create the pad
|
||||||
from the pad template that has been registered with the element class in
|
from the pad template that has been registered with the element class in
|
||||||
the <function>_base_init ()</function> function. After creating the pad,
|
the <function>_base_init ()</function> function. After creating the pad,
|
||||||
you have to set a <function>_link ()</function> function pointer and a
|
you have to set a <function>_setcaps ()</function> function pointer and
|
||||||
<function>_getcaps ()</function> function pointer. Optionally, you can
|
optionally a <function>_getcaps ()</function> function pointer. Also, you
|
||||||
set a <function>_chain ()</function> function pointer (on sink pads in
|
have to set a <function>_chain ()</function> function pointer.
|
||||||
filter and sink elements) through which data will come in to the element,
|
Alternatively, pads can also operate in looping mode, which mans that they
|
||||||
or (on source pads in source elements) a <function>_get ()</function>
|
can pull data themselves. More on this topic later. After that, you have
|
||||||
function pointer through which data will be pulled from the element. After
|
to register the pad with the element. This happens like this:
|
||||||
that, you have to register the pad with the element. This happens like
|
|
||||||
this:
|
|
||||||
</para>
|
</para>
|
||||||
<programlisting><!-- example-begin init.func a --><!--
|
<programlisting><!-- example-begin init.func a --><!--
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
@ -76,11 +73,11 @@ gst_my_filter_class_init (GstMyFilterClass * klass)
|
||||||
<!-- example-begin init.func b -->
|
<!-- example-begin init.func b -->
|
||||||
static gboolean gst_my_filter_setcaps (GstPad *pad,
|
static gboolean gst_my_filter_setcaps (GstPad *pad,
|
||||||
GstCaps *caps);
|
GstCaps *caps);
|
||||||
static GstCaps * gst_my_filter_getcaps (GstPad *pad);
|
|
||||||
static GstFlowReturn gst_my_filter_chain (GstPad *pad,
|
static GstFlowReturn gst_my_filter_chain (GstPad *pad,
|
||||||
GstBuffer *buf);
|
GstBuffer *buf);
|
||||||
<!-- example-end init.func b -->
|
<!-- example-end init.func b -->
|
||||||
<!-- example-begin init.func c --><!--
|
<!-- example-begin init.func c --><!--
|
||||||
|
static GstCaps * gst_my_filter_getcaps (GstPad *pad);
|
||||||
static gboolean gst_my_filter_event (GstPad *pad,
|
static gboolean gst_my_filter_event (GstPad *pad,
|
||||||
GstEvent *event);
|
GstEvent *event);
|
||||||
--><!-- example-end init.func c -->
|
--><!-- example-end init.func c -->
|
||||||
|
@ -95,10 +92,10 @@ gst_my_filter_init (GstMyFilter *filter)
|
||||||
filter->sinkpad = gst_pad_new_from_template (
|
filter->sinkpad = gst_pad_new_from_template (
|
||||||
gst_element_class_get_pad_template (klass, "sink"), "sink");
|
gst_element_class_get_pad_template (klass, "sink"), "sink");
|
||||||
gst_pad_set_setcaps_function (filter->sinkpad, gst_my_filter_setcaps);
|
gst_pad_set_setcaps_function (filter->sinkpad, gst_my_filter_setcaps);
|
||||||
gst_pad_set_getcaps_function (filter->sinkpad, gst_my_filter_getcaps);
|
|
||||||
gst_pad_set_chain_function (filter->sinkpad, gst_my_filter_chain);
|
gst_pad_set_chain_function (filter->sinkpad, gst_my_filter_chain);
|
||||||
<!-- example-end init.func d -->
|
<!-- example-end init.func d -->
|
||||||
<!-- example-begin init.func e --><!--
|
<!-- example-begin init.func e --><!--
|
||||||
|
gst_pad_set_getcaps_function (filter->sinkpad, gst_my_filter_getcaps);
|
||||||
gst_pad_set_event_function (filter->sinkpad, gst_my_filter_event);
|
gst_pad_set_event_function (filter->sinkpad, gst_my_filter_event);
|
||||||
--><!-- example-end init.func e -->
|
--><!-- example-end init.func e -->
|
||||||
<!-- example-begin init.func f -->
|
<!-- example-begin init.func f -->
|
||||||
|
@ -107,30 +104,33 @@ gst_my_filter_init (GstMyFilter *filter)
|
||||||
/* pad through which data goes out of the element */
|
/* pad through which data goes out of the element */
|
||||||
filter->srcpad = gst_pad_new_from_template (
|
filter->srcpad = gst_pad_new_from_template (
|
||||||
gst_element_class_get_pad_template (klass, "src"), "src");
|
gst_element_class_get_pad_template (klass, "src"), "src");
|
||||||
gst_pad_set_setcaps_function (filter->srcpad, gst_my_filter_setcaps);
|
<!-- example-end init.func f -->
|
||||||
|
<!-- example-begin init.func g --><!--
|
||||||
gst_pad_set_getcaps_function (filter->srcpad, gst_my_filter_getcaps);
|
gst_pad_set_getcaps_function (filter->srcpad, gst_my_filter_getcaps);
|
||||||
|
--><!-- example-end init.func g -->
|
||||||
|
<!-- example-begin init.func h -->
|
||||||
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
|
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
|
||||||
|
|
||||||
/* properties initial value */
|
/* properties initial value */
|
||||||
filter->silent = FALSE;
|
filter->silent = FALSE;
|
||||||
}
|
}
|
||||||
<!-- example-end init.func f --></programlisting>
|
<!-- example-end init.func h --></programlisting>
|
||||||
|
|
||||||
<sect1 id="section-pads-linkfn" xreflabel="The link function">
|
<sect1 id="section-pads-linkfn" xreflabel="The link function">
|
||||||
<title>The link function</title>
|
<title>The setcaps-function</title>
|
||||||
<para>
|
<para>
|
||||||
The <function>_link ()</function> is called during caps negotiation. This
|
The <function>_setcaps ()</function>-function is called during caps
|
||||||
is the process where the linked pads decide on the streamtype that will
|
negotiation, which is discussed in great detail in <xref
|
||||||
transfer between them. A full list of type-definitions can be found in
|
linkend="chapter-negotiation"/>. This is the process where the linked
|
||||||
<xref linkend="chapter-building-types"/>. A <function>_link ()</function>
|
pads decide on the streamtype that will transfer between them. A full
|
||||||
|
list of type-definitions can be found in <xref
|
||||||
|
linkend="chapter-building-types"/>. A <function>_link ()</function>
|
||||||
receives a pointer to a <ulink type="http"
|
receives a pointer to a <ulink type="http"
|
||||||
url="../../gstreamer/html/gstreamer-GstCaps.html"><classname>GstCaps</classname>
|
url="../../gstreamer/html/gstreamer-GstCaps.html"><classname>GstCaps</classname></ulink>
|
||||||
</ulink> struct that defines the proposed streamtype, and can respond with
|
struct that defines the proposed streamtype, and can respond with
|
||||||
either <quote>yes</quote> (<symbol>GST_PAD_LINK_OK</symbol>),
|
either <quote>yes</quote> (<symbol>TRUE</symbol>) or <quote>no</quote>
|
||||||
<quote>no</quote> (<symbol>GST_PAD_LINK_REFUSED</symbol>) or
|
(<symbol>FALSE</symbol>). If the element responds positively towards
|
||||||
<quote>don't know yet</quote> (<symbol>GST_PAD_LINK_DELAYED</symbol>).
|
the streamtype, that type will be used on the pad. An example:
|
||||||
If the element responds positively towards the streamtype, that type
|
|
||||||
will be used on the pad. An example:
|
|
||||||
</para>
|
</para>
|
||||||
<programlisting><!-- example-begin caps.func a -->
|
<programlisting><!-- example-begin caps.func a -->
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -138,10 +138,7 @@ gst_my_filter_setcaps (GstPad *pad,
|
||||||
GstCaps *caps)
|
GstCaps *caps)
|
||||||
{
|
{
|
||||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||||
GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
GstPad *otherpad = (pad == filter->srcpad) ? filter->sinkpad :
|
|
||||||
filter->srcpad;
|
|
||||||
GstPadLinkReturn ret;
|
|
||||||
const gchar *mime;
|
const gchar *mime;
|
||||||
|
|
||||||
/* Since we're an audio filter, we want to handle raw audio
|
/* Since we're an audio filter, we want to handle raw audio
|
||||||
|
@ -157,9 +154,8 @@ gst_my_filter_setcaps (GstPad *pad,
|
||||||
/* we're a filter and don't touch the properties of the data.
|
/* we're a filter and don't touch the properties of the data.
|
||||||
* That means we can set the given caps unmodified on the next
|
* That means we can set the given caps unmodified on the next
|
||||||
* element, and use that negotiation return value as ours. */
|
* element, and use that negotiation return value as ours. */
|
||||||
ret = gst_pad_set_caps (otherpad, gst_caps_copy (caps));
|
if (!gst_pad_set_caps (filter->srcpad, caps))
|
||||||
if (GST_PAD_LINK_FAILED (ret))
|
return FALSE;
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Capsnego succeeded, get the stream properties for internal
|
/* Capsnego succeeded, get the stream properties for internal
|
||||||
* usage and return success. */
|
* usage and return success. */
|
||||||
|
@ -169,9 +165,21 @@ gst_my_filter_setcaps (GstPad *pad,
|
||||||
g_print ("Caps negotiation succeeded with %d Hz @ %d channels\n",
|
g_print ("Caps negotiation succeeded with %d Hz @ %d channels\n",
|
||||||
filter->samplerate, filter->channels);
|
filter->samplerate, filter->channels);
|
||||||
|
|
||||||
return ret;
|
return TRUE;
|
||||||
}
|
}
|
||||||
<!-- example-end caps.func a --></programlisting>
|
<!-- example-end caps.func a -->
|
||||||
|
<!-- example-begin caps.func b --><!--
|
||||||
|
static GstCaps *
|
||||||
|
gst_my_filter_getcaps (GstPad * pad)
|
||||||
|
{
|
||||||
|
GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
|
||||||
|
GstPad *otherpad = (pad == filter->srcpad) ? filter->sinkpad :
|
||||||
|
filter->srcpad;
|
||||||
|
GstCaps *othercaps = gst_pad_get_allowed_caps (otherpad);
|
||||||
|
|
||||||
|
return othercaps;
|
||||||
|
}
|
||||||
|
--><!-- example-end caps.func b --></programlisting>
|
||||||
<para>
|
<para>
|
||||||
In here, we check the mimetype of the provided caps. Normally, you don't
|
In here, we check the mimetype of the provided caps. Normally, you don't
|
||||||
need to do that in your own plugin/element, because the core does that
|
need to do that in your own plugin/element, because the core does that
|
||||||
|
@ -188,58 +196,11 @@ gst_my_filter_setcaps (GstPad *pad,
|
||||||
<para>
|
<para>
|
||||||
If your <function>_link ()</function> function does not need to perform
|
If your <function>_link ()</function> function does not need to perform
|
||||||
any specific operation (i.e. it will only forward caps), you can set it
|
any specific operation (i.e. it will only forward caps), you can set it
|
||||||
to <function>gst_pad_proxy_link</function>. This is a link forwarding
|
to <function>gst_pad_proxy_link ()</function>. This is a link forwarding
|
||||||
function implementation provided by the core. It is useful for elements
|
function implementation provided by the core. It is useful for elements
|
||||||
such as <classname>identity</classname>.
|
such as <classname>identity</classname>.
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="section-pads-getcapsfn" xreflabel="The getcaps function">
|
|
||||||
<title>The getcaps function</title>
|
|
||||||
<para>
|
|
||||||
The <function>_getcaps ()</function> function is used to request the list
|
|
||||||
of supported formats and properties from the element. In some cases, this
|
|
||||||
will be equal to the formats provided by the pad template, in which case
|
|
||||||
this function can be omitted. In some cases, too, it will not depend on
|
|
||||||
anything inside this element, but it will rather depend on the input from
|
|
||||||
another element linked to this element's sink or source pads. In that case,
|
|
||||||
you can use <function>gst_pad_proxy_getcaps</function> as implementation,
|
|
||||||
it provides getcaps forwarding in the core. However, in many cases, the
|
|
||||||
format supported by this element cannot be defined externally, but is
|
|
||||||
more specific than those provided by the pad template. In this case, you
|
|
||||||
should use a <function>_getcaps ()</function> function. In the case as
|
|
||||||
specified below, we assume that our filter is able to resample sound, so
|
|
||||||
it would be able to provide any samplerate (indifferent from the samplerate
|
|
||||||
specified on the other pad) on both pads. It explains how a
|
|
||||||
<function>_getcaps ()</function> can be used to do this.
|
|
||||||
</para>
|
|
||||||
<programlisting><!-- example-begin caps.func b -->
|
|
||||||
static GstCaps *
|
|
||||||
gst_my_filter_getcaps (GstPad *pad)
|
|
||||||
{
|
|
||||||
GstMyFilter *filter = GST_MY_FILTER (gst_pad_get_parent (pad));
|
|
||||||
GstPad *otherpad = (pad == filter->srcpad) ? filter->sinkpad :
|
|
||||||
filter->srcpad;
|
|
||||||
GstCaps *othercaps = gst_pad_get_allowed_caps (otherpad), *caps;
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
if (gst_caps_is_empty (othercaps))
|
|
||||||
return othercaps;
|
|
||||||
|
|
||||||
/* We support *any* samplerate, indifferent from the samplerate
|
|
||||||
* supported by the linked elements on both sides. */
|
|
||||||
for (i = 0; i < gst_caps_get_size (othercaps); i++) {
|
|
||||||
GstStructure *structure = gst_caps_get_structure (othercaps, i);
|
|
||||||
|
|
||||||
gst_structure_remove_field (structure, "rate");
|
|
||||||
}
|
|
||||||
caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
|
|
||||||
gst_caps_unref (othercaps);
|
|
||||||
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
<!-- example-end caps.func b --></programlisting>
|
|
||||||
</sect1>
|
|
||||||
<!-- example-begin pads.c --><!--
|
<!-- example-begin pads.c --><!--
|
||||||
#include "init.func"
|
#include "init.func"
|
||||||
#include "caps.func"
|
#include "caps.func"
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<!ENTITY BUILDING_TESTAPP SYSTEM "building-testapp.xml">
|
<!ENTITY BUILDING_TESTAPP SYSTEM "building-testapp.xml">
|
||||||
<!ENTITY BUILDING_FILTERFACT SYSTEM "building-filterfactory.xml">
|
<!ENTITY BUILDING_FILTERFACT SYSTEM "building-filterfactory.xml">
|
||||||
|
|
||||||
|
<!ENTITY ADVANCED_NEGOTIATION SYSTEM "advanced-negotiation.xml">
|
||||||
<!ENTITY ADVANCED_SCHEDULING SYSTEM "advanced-scheduling.xml">
|
<!ENTITY ADVANCED_SCHEDULING SYSTEM "advanced-scheduling.xml">
|
||||||
<!ENTITY ADVANCED_TYPES SYSTEM "advanced-types.xml">
|
<!ENTITY ADVANCED_TYPES SYSTEM "advanced-types.xml">
|
||||||
<!ENTITY ADVANCED_REQUEST SYSTEM "advanced-request.xml">
|
<!ENTITY ADVANCED_REQUEST SYSTEM "advanced-request.xml">
|
||||||
|
@ -136,6 +137,7 @@
|
||||||
</para>
|
</para>
|
||||||
</partintro>
|
</partintro>
|
||||||
|
|
||||||
|
&ADVANCED_NEGOTIATION;
|
||||||
&ADVANCED_SCHEDULING;
|
&ADVANCED_SCHEDULING;
|
||||||
&ADVANCED_TYPES;
|
&ADVANCED_TYPES;
|
||||||
&ADVANCED_REQUEST;
|
&ADVANCED_REQUEST;
|
||||||
|
|
|
@ -7,8 +7,12 @@ libchain_la_SOURCES = chain.c
|
||||||
libchain2_la_SOURCES = chain2.c
|
libchain2_la_SOURCES = chain2.c
|
||||||
libstate_la_SOURCES = state.c
|
libstate_la_SOURCES = state.c
|
||||||
libproperties_la_SOURCES = properties.c
|
libproperties_la_SOURCES = properties.c
|
||||||
|
libforwardcaps_la_SOURCES = forwardcaps.c
|
||||||
|
libconvertcaps_la_SOURCES = convertcaps.c
|
||||||
|
libgetcaps_la_SOURCES = getcaps.c
|
||||||
DISTCLEANFILES = \
|
DISTCLEANFILES = \
|
||||||
boilerplate.c pads.c chain.c chain2.c state.c properties.c \
|
boilerplate.c pads.c chain.c chain2.c state.c properties.c \
|
||||||
|
forwardcaps.c convertcaps.c getcaps.c \
|
||||||
init.func caps.func chain.func state.func register.func filter.h
|
init.func caps.func chain.func state.func register.func filter.h
|
||||||
|
|
||||||
EXTRA_DIST = extract.pl
|
EXTRA_DIST = extract.pl
|
||||||
|
@ -19,7 +23,10 @@ EXAMPLES = \
|
||||||
libchain.la \
|
libchain.la \
|
||||||
libchain2.la \
|
libchain2.la \
|
||||||
libstate.la \
|
libstate.la \
|
||||||
libproperties.la
|
libproperties.la \
|
||||||
|
libforwardcaps.la \
|
||||||
|
libconvertcaps.la \
|
||||||
|
libgetcaps.la
|
||||||
|
|
||||||
EXAMPLE_APPS = \
|
EXAMPLE_APPS = \
|
||||||
test
|
test
|
||||||
|
@ -64,6 +71,10 @@ test.c: $(top_srcdir)/docs/pwg/building-testapp.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
||||||
$(top_srcdir)/docs/pwg/building-testapp.xml
|
$(top_srcdir)/docs/pwg/building-testapp.xml
|
||||||
|
|
||||||
|
forwardcaps.c convertcaps.c getcaps.c: $(top_srcdir)/docs/pwg/advanced-negotiation.xml init.func register.func chain.func state.func
|
||||||
|
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
||||||
|
$(top_srcdir)/docs/pwg/advanced-negotiation.xml
|
||||||
|
|
||||||
noinst_PROGRAMS = $(EXAMPLE_APPS)
|
noinst_PROGRAMS = $(EXAMPLE_APPS)
|
||||||
noinst_LTLIBRARIES = $(EXAMPLES)
|
noinst_LTLIBRARIES = $(EXAMPLES)
|
||||||
LDADD = $(GST_OBJ_LIBS)
|
LDADD = $(GST_OBJ_LIBS)
|
||||||
|
|
|
@ -7,8 +7,12 @@ libchain_la_SOURCES = chain.c
|
||||||
libchain2_la_SOURCES = chain2.c
|
libchain2_la_SOURCES = chain2.c
|
||||||
libstate_la_SOURCES = state.c
|
libstate_la_SOURCES = state.c
|
||||||
libproperties_la_SOURCES = properties.c
|
libproperties_la_SOURCES = properties.c
|
||||||
|
libforwardcaps_la_SOURCES = forwardcaps.c
|
||||||
|
libconvertcaps_la_SOURCES = convertcaps.c
|
||||||
|
libgetcaps_la_SOURCES = getcaps.c
|
||||||
DISTCLEANFILES = \
|
DISTCLEANFILES = \
|
||||||
boilerplate.c pads.c chain.c chain2.c state.c properties.c \
|
boilerplate.c pads.c chain.c chain2.c state.c properties.c \
|
||||||
|
forwardcaps.c convertcaps.c getcaps.c \
|
||||||
init.func caps.func chain.func state.func register.func filter.h
|
init.func caps.func chain.func state.func register.func filter.h
|
||||||
|
|
||||||
EXTRA_DIST = extract.pl
|
EXTRA_DIST = extract.pl
|
||||||
|
@ -19,7 +23,10 @@ EXAMPLES = \
|
||||||
libchain.la \
|
libchain.la \
|
||||||
libchain2.la \
|
libchain2.la \
|
||||||
libstate.la \
|
libstate.la \
|
||||||
libproperties.la
|
libproperties.la \
|
||||||
|
libforwardcaps.la \
|
||||||
|
libconvertcaps.la \
|
||||||
|
libgetcaps.la
|
||||||
|
|
||||||
EXAMPLE_APPS = \
|
EXAMPLE_APPS = \
|
||||||
test
|
test
|
||||||
|
@ -64,6 +71,10 @@ test.c: $(top_srcdir)/docs/pwg/building-testapp.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
||||||
$(top_srcdir)/docs/pwg/building-testapp.xml
|
$(top_srcdir)/docs/pwg/building-testapp.xml
|
||||||
|
|
||||||
|
forwardcaps.c convertcaps.c getcaps.c: $(top_srcdir)/docs/pwg/advanced-negotiation.xml init.func register.func chain.func state.func
|
||||||
|
$(PERL_PATH) $(srcdir)/extract.pl $@ \
|
||||||
|
$(top_srcdir)/docs/pwg/advanced-negotiation.xml
|
||||||
|
|
||||||
noinst_PROGRAMS = $(EXAMPLE_APPS)
|
noinst_PROGRAMS = $(EXAMPLE_APPS)
|
||||||
noinst_LTLIBRARIES = $(EXAMPLES)
|
noinst_LTLIBRARIES = $(EXAMPLES)
|
||||||
LDADD = $(GST_OBJ_LIBS)
|
LDADD = $(GST_OBJ_LIBS)
|
||||||
|
|
Loading…
Reference in a new issue