mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
docs/pwg/: Documentation on how to write source and sink elements. Other stuff in chapter 4 (1-to-n/demuxer, n-to-1/m...
Original commit message from CVS: * docs/pwg/other-sink.xml: * docs/pwg/other-source.xml: Documentation on how to write source and sink elements. Other stuff in chapter 4 (1-to-n/demuxer, n-to-1/muxer, n-to-n, manager, autoplugger) are all still pending.
This commit is contained in:
parent
d357e5c959
commit
b988d58ab2
3 changed files with 621 additions and 2 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
2004-03-24 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
|
* docs/pwg/other-sink.xml:
|
||||||
|
* docs/pwg/other-source.xml:
|
||||||
|
Documentation on how to write source and sink elements. Other
|
||||||
|
stuff in chapter 4 (1-to-n/demuxer, n-to-1/muxer, n-to-n,
|
||||||
|
manager, autoplugger) are all still pending.
|
||||||
|
|
||||||
2004-03-25 Benjamin Otte <otte@gnome.org>
|
2004-03-25 Benjamin Otte <otte@gnome.org>
|
||||||
|
|
||||||
* testsuite/elements/Makefile.am:
|
* testsuite/elements/Makefile.am:
|
||||||
|
|
|
@ -4,6 +4,164 @@
|
||||||
<chapter id="chapter-other-sink" xreflabel="Writing a Sink">
|
<chapter id="chapter-other-sink" xreflabel="Writing a Sink">
|
||||||
<title>Writing a Sink</title>
|
<title>Writing a Sink</title>
|
||||||
<para>
|
<para>
|
||||||
FIXME: write.
|
Sinks are output elements that, opposite to sources, have no source
|
||||||
|
pads and one or more (usually one) sink pad. They can be sound card
|
||||||
|
outputs, disk writers, etc. This chapter will discuss the basic
|
||||||
|
implementation of sink elements.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="other-sink-processing" xreflabel="Data processing, events, synchronization and clocks">
|
||||||
|
<title>Data processing, events, synchronization and clocks</title>
|
||||||
|
<para>
|
||||||
|
Except for corner cases, sink elements will be <function>_chain
|
||||||
|
()</function>-based elements. The concept of such elements has
|
||||||
|
been discussed before in detail, so that will be skipped here. What
|
||||||
|
is very important in sink elements, specifically in real-time audio
|
||||||
|
and video sources (such as <classname>osssink</classname> or
|
||||||
|
<classname>ximagesink</classname>), is event handling in the
|
||||||
|
<function>_chain ()</function>-function, because most elements rely
|
||||||
|
on EOS-handling of the sink element, and because A/V synchronization
|
||||||
|
can only be perfect if the element takes this into account.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
How to achieve synchronization between streams depends on whether
|
||||||
|
you're a clock-providing or a clock-receiving element. If you're
|
||||||
|
the clock provider, you can do with time whatever you want. Correct
|
||||||
|
handling would mean that you check whether the end of the previous
|
||||||
|
buffer (if any) and the start of the current buffer are the same.
|
||||||
|
If so, there's no gap between the two and you can continue playing
|
||||||
|
right away. If there is a gap, then you'll need to wait for your
|
||||||
|
clock to reach that time. How to do that depends on the element
|
||||||
|
type. In the case of audio output elements, you would output silence
|
||||||
|
for a while. In the case of video, you would show background color.
|
||||||
|
In case of subtitles, show no subtitles at all.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In the case that the provided clock and the received clock are not
|
||||||
|
the same (or in the case where your element provides no clock, which
|
||||||
|
is the same), you simply wait for the clock to reach the timestamp of
|
||||||
|
the current buffer and then you handle the data in it.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A simple data handling function would look like this:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static void
|
||||||
|
gst_my_sink_chain (GstPad *pad,
|
||||||
|
GstData *data)
|
||||||
|
{
|
||||||
|
GstMySink *sink = GST_MY_SINK (gst_pad_get_parent (pad));
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstClockTime time;
|
||||||
|
|
||||||
|
/* only needed if the element is GST_EVENT_AWARE */
|
||||||
|
if (GST_IS_EVENT (data)) {
|
||||||
|
GstEvent *event = GST_EVENT (data);
|
||||||
|
|
||||||
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_EOS:
|
||||||
|
[ if your element provides a clock, disable (inactivate) it here ]
|
||||||
|
/* pass-through */
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* the default handler handles discontinuities, even if your
|
||||||
|
* element provides a clock! */
|
||||||
|
gst_pad_event_default (pad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = GST_BUFFER (data);
|
||||||
|
if (GST_BUFFER_TIME_IS_VALID (buf))
|
||||||
|
time = GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
else
|
||||||
|
time = sink->expected_next_time;
|
||||||
|
|
||||||
|
/* Synchronization - the property is only useful in case the
|
||||||
|
* element has the option of not syncing. So it is not useful
|
||||||
|
* for hardware-sync (clock-providing) elements. */
|
||||||
|
if (sink->sync) {
|
||||||
|
/* This check is only needed if you provide a clock. Else,
|
||||||
|
* you can always execute the 'else' clause. */
|
||||||
|
if (sink->provided_clock == sink->received_clock) {
|
||||||
|
/* GST_SECOND / 10 is 0,1 sec, it's an arbitrary value. The
|
||||||
|
* casts are needed because else it'll be unsigned and we
|
||||||
|
* won't detect negative values. */
|
||||||
|
if (llabs ((gint64) sink->expected_next_time - (gint64) time) >
|
||||||
|
(GST_SECOND / 10)) {
|
||||||
|
/* so are we ahead or behind? */
|
||||||
|
if (time > sink->expected_time) {
|
||||||
|
/* we need to wait a while... In case of audio, output
|
||||||
|
* silence. In case of video, output background color.
|
||||||
|
* In case of subtitles, display nothing. */
|
||||||
|
[..]
|
||||||
|
} else {
|
||||||
|
/* Drop data. */
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* You could do more sophisticated things here, but we'll
|
||||||
|
* keep it simple for the purpose of the example. */
|
||||||
|
gst_element_wait (GST_ELEMENT (sink), time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And now handle the data. */
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="other-sink-buffers" xreflabel="Special memory">
|
||||||
|
<title>Special memory</title>
|
||||||
|
<para>
|
||||||
|
Like source elements, sink elements can sometimes provide externally
|
||||||
|
allocated (such as X-provided or DMA'able) memory to elements earlier
|
||||||
|
in the pipeline, and thereby prevent the need for
|
||||||
|
<function>memcpy ()</function> for incoming data. We do this by
|
||||||
|
providing a pad-allocate-buffer function.
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static GstBuffer * gst_my_sink_buffer_allocate (GstPad *pad,
|
||||||
|
guint64 offset,
|
||||||
|
guint size);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_sink_init (GstMySink *sink)
|
||||||
|
{
|
||||||
|
[..]
|
||||||
|
gst_pad_set_bufferalloc_function (sink->sinkpad,
|
||||||
|
gst_my_sink_buffer_allocate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_sink_buffer_free (GstBuffer *buf)
|
||||||
|
{
|
||||||
|
GstMySink *sink = GST_MY_SINK (GST_BUFFER_PRIVATE (buf));
|
||||||
|
|
||||||
|
/* Do whatever is needed here. */
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstBuffer *
|
||||||
|
gst_my_sink_buffer_allocate (GstPad *pad,
|
||||||
|
guint64 offset,
|
||||||
|
guint size)
|
||||||
|
{
|
||||||
|
GstBuffer *buf = gst_buffer_new ();
|
||||||
|
|
||||||
|
/* So here it's up to you to wrap your private buffers and
|
||||||
|
* return that. */
|
||||||
|
GST_BUFFER_FREE_DATA_FUNC (buf) = gst_my_sink_buffer_free;
|
||||||
|
GST_BUFFER_PRIVATE (buf) = sink;
|
||||||
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_DONTFREE);
|
||||||
|
[..]
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
@ -4,6 +4,459 @@
|
||||||
<chapter id="chapter-other-source" xreflabel="Writing a Source">
|
<chapter id="chapter-other-source" xreflabel="Writing a Source">
|
||||||
<title>Writing a Source</title>
|
<title>Writing a Source</title>
|
||||||
<para>
|
<para>
|
||||||
FIXME: write.
|
Source elements are the start of a data streaming pipeline. Source
|
||||||
|
elements have no sink pads and have one or more source pads. We will
|
||||||
|
focus on single-sourcepad elements here, but the concepts apply equally
|
||||||
|
well to multi-sourcepad elements. This chapter will explain the essentials
|
||||||
|
of source elements, which features it should implement and which it
|
||||||
|
doesn't have to, and how source elements will interact with other
|
||||||
|
elements in a pipeline.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<sect1 id="section-source-getfn" xreflabel="The get()-function">
|
||||||
|
<title>The get()-function</title>
|
||||||
|
<para>
|
||||||
|
Source elements have the special option of having a
|
||||||
|
<function>_get ()</function>-function rather than a
|
||||||
|
<function>_loop ()</function>- or <function>_chain
|
||||||
|
()</function>-function. A <function>_get ()</function>-function is
|
||||||
|
called by the scheduler every time the next elements needs data. Apart
|
||||||
|
from corner cases, every source element will want to be <function>_get
|
||||||
|
()</function>-based.
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static GstData * gst_my_source_get (GstPad *pad);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_init (GstMySource *src)
|
||||||
|
{
|
||||||
|
[..]
|
||||||
|
gst_pad_set_get_function (src->srcpad, gst_my_source_get);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstData *
|
||||||
|
gst_my_source_get (GstPad *pad)
|
||||||
|
{
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
|
buffer = gst_buffer_new ();
|
||||||
|
GST_BUFFER_DATA (buf) = g_strdup ("hello pipeline!");
|
||||||
|
GST_BUFFER_SIZE (buf) = strlen (GST_BUFFER_DATA (buf));
|
||||||
|
/* terminating '/0' */
|
||||||
|
GST_BUFFER_MAZSIZE (buf) = GST_BUFFER_SIZE (buf) + 1;
|
||||||
|
|
||||||
|
return GST_DATA (buffer);
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-source-padfn" xreflabel="Events, querying and converting">
|
||||||
|
<title>Events, querying and converting</title>
|
||||||
|
<para>
|
||||||
|
One of the most important functions of source elements is to
|
||||||
|
implement correct query, convert and event handling functions.
|
||||||
|
Those will continuously describe the current state of the stream.
|
||||||
|
Query functions can be used to get stream properties such as current
|
||||||
|
position and length. This can be used by fellow elements to convert
|
||||||
|
this same value into a different unit, or by appliations to provide
|
||||||
|
information about the length/position of the stream to the user.
|
||||||
|
Conversion functions are used to convert such values from one unit
|
||||||
|
to another. Lastly, events are mostly used to seek to positions
|
||||||
|
inside the stream. Any function is essentially optional, but the
|
||||||
|
element should try to provide as much information as it knows. Note
|
||||||
|
that elements providing an event function should also list their
|
||||||
|
supported events in an <function>_get_event_mask ()</function>
|
||||||
|
function. Elements supporting query operations should list the
|
||||||
|
supported operations in a <function>_get_query_types
|
||||||
|
()</function> function. Elements supporting either conversion
|
||||||
|
or query operations should also implement a <function>_get_formats
|
||||||
|
()</function> function.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
An example source element could, for example, be an element that
|
||||||
|
continuously generates a wave tone at 44,1 kHz, mono, 16-bit. This
|
||||||
|
element will generate 44100 audio samples per second or 88,2 kB/s.
|
||||||
|
This information can be used to implement such functions:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static GstFormat * gst_my_source_format_list (GstPad *pad);
|
||||||
|
static GstQueryType * gst_my_source_query_list (GstPad *pad);
|
||||||
|
|
||||||
|
static gboolean gst_my_source_convert (GstPad *pad,
|
||||||
|
GstFormat from_fmt,
|
||||||
|
gint64 from_val,
|
||||||
|
GstFormat *to_fmt,
|
||||||
|
gint64 *to_val);
|
||||||
|
static gboolean gst_my_source_query (GstPad *pad,
|
||||||
|
GstQueryType type,
|
||||||
|
GstFormat *to_fmt,
|
||||||
|
gint64 *to_val);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_init (GstMySource *src)
|
||||||
|
{
|
||||||
|
[..]
|
||||||
|
gst_pad_set_convert_function (src->srcpad, gst_my_source_convert);
|
||||||
|
gst_pad_set_formats_function (src->srcpad, gst_my_source_format_list);
|
||||||
|
gst_pad_set_query_function (src->srcpad, gst_my_source_query);
|
||||||
|
gst_pad_set_query_type_function (src->srcpad, gst_my_source_query_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function returns an enumeration of supported GstFormat
|
||||||
|
* types in the query() or convert() functions. See gst/gstformat.h
|
||||||
|
* for a full list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static GstFormat *
|
||||||
|
gst_my_source_format_list (GstPad *pad)
|
||||||
|
{
|
||||||
|
static const GstFormat formats[] = {
|
||||||
|
GST_FORMAT_TIME,
|
||||||
|
GST_FORMAT_DEFAULT, /* means "audio samples" */
|
||||||
|
GST_FORMAT_BYTES,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
return formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function returns an enumeration of the supported query()
|
||||||
|
* operations. Since we generate audio internally, we only provide
|
||||||
|
* an indication of how many samples we've played so far. File sources
|
||||||
|
* or such elements could also provide GST_QUERY_TOTAL for the total
|
||||||
|
* stream length, or other things. See gst/gstquery.h for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static GstQueryType *
|
||||||
|
gst_my_source_query_list (GstPad *pad)
|
||||||
|
{
|
||||||
|
static const GstQueryType query_types[] = {
|
||||||
|
GST_QUERY_POSITION,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return query_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And below are the logical implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_my_source_convert (GstPad *pad,
|
||||||
|
GstFormat from_fmt,
|
||||||
|
gint64 from_val,
|
||||||
|
GstFormat *to_fmt,
|
||||||
|
gint64 *to_val)
|
||||||
|
{
|
||||||
|
gboolean res = TRUE;
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
switch (from_fmt) {
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
switch (*to_fmt) {
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
/* nothing */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_BYTES:
|
||||||
|
*to_val = from_val / (GST_SECOND / (44100 * 2));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_DEFAULT:
|
||||||
|
*to_val = from_val / (GST_SECOND / 44100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_BYTES:
|
||||||
|
switch (*to_fmt) {
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
*to_val = from_val * (GST_SECOND / (44100 * 2));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_BYTES:
|
||||||
|
/* nothing */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_DEFAULT:
|
||||||
|
*to_val = from_val / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_DEFAULT:
|
||||||
|
switch (*to_fmt) {
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
*to_val = from_val * (GST_SECOND / 44100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_BYTES:
|
||||||
|
*to_val = from_val * 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_FORMAT_DEFAULT:
|
||||||
|
/* nothing */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_my_source_query (GstPad *pad,
|
||||||
|
GstQueryType type,
|
||||||
|
GstFormat *to_fmt,
|
||||||
|
gint64 *to_val)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
gboolean res = TRUE;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GST_QUERY_POSITION:
|
||||||
|
res = gst_pad_convert (pad, GST_FORMAT_BYTES, src->total_bytes,
|
||||||
|
to_fmt, to_val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
Be sure to increase src->total_bytes after each call to your
|
||||||
|
<function>_get ()</function> function.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Event handling has already been explained previously in the events
|
||||||
|
chapter.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-source-sync" xreflabel="Time, clocking and synchronization">
|
||||||
|
<title>Time, clocking and synchronization</title>
|
||||||
|
<para>
|
||||||
|
The above example does not provide any timing info, but will suffice
|
||||||
|
for elementary data sources such as a file source or network data
|
||||||
|
source element. Things become slightly more complicated, but still
|
||||||
|
very simple, if we create artificial video or audio data sources,
|
||||||
|
such as a video test image source or an artificial audio source (e.g.
|
||||||
|
<classname>sinesrc</classname> or <classname>silence</classname>).
|
||||||
|
It will become more complicated if we want the element to be a
|
||||||
|
realtime capture source, such as a video4linux source (for reading
|
||||||
|
video frames from a TV card) or an ALSA source (for reading data
|
||||||
|
from soundcards supported by an ALSA-driver). Here, we will need to
|
||||||
|
make the element aware of timing and clocking.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Timestamps can essentially be generated from all the information
|
||||||
|
given above without any difficulty. We could add a very small amount
|
||||||
|
of code to generate perfectly timestamped buffers from our
|
||||||
|
<function>_get ()</function>-function:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static void
|
||||||
|
gst_my_source_init (GstMySource *src)
|
||||||
|
{
|
||||||
|
[..]
|
||||||
|
src->total_bytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstData *
|
||||||
|
gst_my_source_get (GstPad *pad)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstFormat fmt = GST_FORMAT_TIME;
|
||||||
|
[..]
|
||||||
|
GST_BUFFER_DURATION (buf) = GST_BUFFER_SIZE (buf) * (GST_SECOND / (44100 * 2));
|
||||||
|
GST_BUFFER_TIMESTAMP (buf) = src->total_bytes * (GST_SECOND / (44100 * 2));
|
||||||
|
src->total_bytes += GST_BUFFER_SIZE (buf);
|
||||||
|
|
||||||
|
return GST_DATA (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateReturn
|
||||||
|
gst_my_source_change_state (GstElement *element)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (element);
|
||||||
|
|
||||||
|
switch (GST_STATE_PENDING (element)) {
|
||||||
|
case GT_STATE_PAUSED_TO_READY:
|
||||||
|
src->total_bytes = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_ELEMENT_CLASS (parent_class)->change_state)
|
||||||
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
||||||
|
|
||||||
|
return GST_STATE_SUCCESS;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
That wasn't too hard. Now, let's assume real-time elements. Those
|
||||||
|
can either have hardware-timing, in which case we can rely on backends
|
||||||
|
to provide sync for us (in which case you probably want to provide a
|
||||||
|
clock), or we will have to emulate that internally (e.g. to acquire
|
||||||
|
sync in artificial data elements such as <classname>sinesrc</classname>).
|
||||||
|
Let's first look at the second option (software sync). The first option
|
||||||
|
(hardware sync + providing a clock) does not require any special code
|
||||||
|
with respect to timing, and the clocking section already explained how
|
||||||
|
to provide a clock.
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
enum {
|
||||||
|
ARG_0,
|
||||||
|
[..]
|
||||||
|
ARG_SYNC,
|
||||||
|
[..]
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_class_init (GstMySourceClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
[..]
|
||||||
|
g_object_class_install_property (object_class, ARG_SYNC,
|
||||||
|
g_param_spec_boolean ("sync", "Sync", "Synchronize to clock",
|
||||||
|
FALSE, G_PARAM_READWRITE));
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_init (GstMySource *src)
|
||||||
|
{
|
||||||
|
[..]
|
||||||
|
src->sync = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_get (GstPad *pad)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
GstBuffer *buf;
|
||||||
|
[..]
|
||||||
|
if (src->sync) {
|
||||||
|
/* wait on clock */
|
||||||
|
gst_element_wait (GST_ELEMENT (src), GST_BUFFER_TIMESTAMP (buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GST_DATA (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
GValue *value)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
[..]
|
||||||
|
case ARG_SYNC:
|
||||||
|
g_value_set_boolean (value, src->sync);
|
||||||
|
break;
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
const GValue *value)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
[..]
|
||||||
|
case ARG_SYNC:
|
||||||
|
src->sync = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
Most of this is GObject wrapping code. The actual code to do
|
||||||
|
software-sync (in the <function>_get ()</function>-function)
|
||||||
|
is relatively small.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
<sect1 id="section-source-buffers" xreflabel="Using special memory">
|
||||||
|
<title>Using special memory</title>
|
||||||
|
<para>
|
||||||
|
In some cases, it might be useful to use specially allocated memory
|
||||||
|
(e.g. <function>mmap ()</function>'ed DMA'able memory) in
|
||||||
|
your buffers, and those will require special handling when they are
|
||||||
|
being dereferenced. For this, &GStreamer; uses the concept of
|
||||||
|
buffer-free functions. Those are special functions pointers that an
|
||||||
|
element can set on buffers that it created itself. The given function
|
||||||
|
will be called when the buffer has been dereferenced, so that the
|
||||||
|
element can clean up or re-use memory internally rather than using
|
||||||
|
the default implementation (which simply calls
|
||||||
|
<function>g_free ()</function> on the data pointer).
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
static void
|
||||||
|
gst_my_source_buffer_free (GstBuffer *buf)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (GST_BUFFER_PRIVATE (buf));
|
||||||
|
|
||||||
|
/* do useful things here, like re-queueing the buffer which
|
||||||
|
* makes it available for DMA again. The default handler will
|
||||||
|
* not free this buffer because of the GST_BUFFER_DONTFREE
|
||||||
|
* flag. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_my_source_get (GstPad *pad)
|
||||||
|
{
|
||||||
|
GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
|
||||||
|
GstBuffer *buf;
|
||||||
|
[..]
|
||||||
|
buf = gst_buffer_new ();
|
||||||
|
GST_BUFFER_FREE_DATA_FUNC (buf) = gst_my_source_buffer_free;
|
||||||
|
GST_BUFFER_PRIVATE (buf) = src;
|
||||||
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_READONLY | GST_BUFFER_DONTFREE);
|
||||||
|
[..]
|
||||||
|
|
||||||
|
return GST_DATA (buf);
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
Note that this concept should <emphasis>not</emphasis> be used to
|
||||||
|
decrease the number of calls made to functions such as
|
||||||
|
<function>g_malloc ()</function> inside your element. We
|
||||||
|
have better ways of doing that elsewhere (&GStreamer; core, Glib,
|
||||||
|
Glibc, Linux kernel, etc.).
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
Loading…
Reference in a new issue