Writing a Sink
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.
Data processing, events, synchronization and clocks
Except for corner cases, sink elements will be _chain
()-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 osssink or
ximagesink), is event handling in the
_chain ()-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.
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.
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.
A simple data handling function would look like this:
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. */
[..]
}
Special memory
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
memcpy () for incoming data. We do this by
providing a pad-allocate-buffer function.
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;
}