<!-- ############ chapter ############# --> <chapter id="chapter-other-sink" xreflabel="Writing a Sink"> <title>Writing a Sink</title> <para> 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> <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>