<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <HTML ><HEAD ><TITLE >How a loopfunc works</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK REL="HOME" TITLE="GStreamer Plugin Writer's Guide" HREF="index.html"><LINK REL="UP" TITLE="Advanced Filter Concepts" HREF="part-advanced.html"><LINK REL="PREVIOUS" TITLE="The Optimal Scheduler" HREF="section-sched-opt.html"><LINK REL="NEXT" TITLE="The Bytestream Object" HREF="section-loopfn-bytestream.html"></HEAD ><BODY CLASS="chapter" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" ><SPAN CLASS="application" >GStreamer</SPAN > Plugin Writer's Guide</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="section-sched-opt.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="section-loopfn-bytestream.html" ACCESSKEY="N" >Next</A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="chapter" ><H1 ><A NAME="chapter-loopbased-loopfn" ></A >Chapter 12. How a loopfunc works</H1 ><P > A <CODE CLASS="function" >_loop ()</CODE > function is a function that is called by the scheduler, but without providing data to the element. Instead, the element will become responsible for acquiring its own data, and it will still be responsible of sending data over to its source pads. This method noticeably complicates scheduling; you should only write loop-based elements when you need to. Normally, chain-based elements are preferred. Examples of elements that <SPAN CLASS="emphasis" ><I CLASS="emphasis" >have</I ></SPAN > to be loop-based are elements with multiple sink pads. Since the scheduler will push data into the pads as it comes (and this might not be synchronous), you will easily get ascynronous data on both pads, which means that the data that arrives on the first pad has a different display timestamp then the data arriving on the second pad at the same time. To get over these issues, you should write such elements in a loop-based form. Other elements that are <SPAN CLASS="emphasis" ><I CLASS="emphasis" >easier</I ></SPAN > to write in a loop-based form than in a chain-based form are demuxers and parsers. It is not required to write such elements in a loop-based form, though. </P ><P > Below is an example of the easiest loop-function that one can write: </P ><PRE CLASS="programlisting" > static void gst_my_filter_loopfunc (GstElement *element); static void gst_my_filter_init (GstMyFilter *filter) { [..] gst_element_set_loopfunc (GST_ELEMENT (filter), gst_my_filter_loopfunc); [..] } static void gst_my_filter_loopfunc (GstElement *element) { GstMyFilter *filter = GST_MY_FILTER (element); GstData *data; /* acquire data */ data = gst_pad_pull (filter->sinkpad); /* send data */ gst_pad_push (filter->srcpad, data); } </PRE ><P > Obviously, this specific example has no single advantage over a chain-based element, so you should never write such elements. However, it's a good introduction to the concept. </P ><DIV CLASS="sect1" ><H1 CLASS="sect1" ><A NAME="section-loopfn-multiinput" >12.1. Multi-Input Elements</A ></H1 ><P > Elements with multiple sink pads need to take manual control over their input to assure that the input is synchronized. The following example code could (should) be used in an aggregator, i.e. an element that takes input from multiple streams and sends it out intermangled. Not really useful in practice, but a good example, again. </P ><PRE CLASS="programlisting" > typedef struct _GstMyFilterInputContext { gboolean eos; GstBuffer *lastbuf; } GstMyFilterInputContext; [..] static void gst_my_filter_init (GstMyFilter *filter) { GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter); GstMyFilterInputContext *context; filter->sinkpad1 = gst_pad_new_from_template ( gst_element_class_get_pad_template (klass, "sink"), "sink_1"); context = g_new0 (GstMyFilterInputContext, 1); gst_pad_set_private_data (filter->sinkpad1, context); [..] filter->sinkpad2 = gst_pad_new_from_template ( gst_element_class_get_pad_template (klass, "sink"), "sink_2"); context = g_new0 (GstMyFilterInputContext, 1); gst_pad_set_private_data (filter->sinkpad2, context); [..] gst_element_set_loopfunc (GST_ELEMENT (filter), gst_my_filter_loopfunc); } [..] static void gst_my_filter_loopfunc (GstElement *element) { GstMyFilter *filter = GST_MY_FILTER (element); GList *padlist; GstMyFilterInputContext *first_context = NULL; /* Go over each sink pad, update the cache if needed, handle EOS * or non-responding streams and see which data we should handle * next. */ for (padlist = gst_element_get_padlist (element); padlist != NULL; padlist = g_list_next (padlist)) { GstPad *pad = GST_PAD (padlist->data); GstMyFilterInputContext *context = gst_pad_get_private_data (pad); if (GST_PAD_IS_SRC (pad)) continue; while (GST_PAD_IS_USABLE (pad) && !context->eos && !context->lastbuf) { GstData *data = gst_pad_pull (pad); if (GST_IS_EVENT (data)) { /* We handle events immediately */ GstEvent *event = GST_EVENT (data); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: context->eos = TRUE; gst_event_unref (event); break; case GST_EVENT_DISCONTINUOUS: g_warning ("HELP! How do I handle this?"); /* fall-through */ default: gst_pad_event_default (pad, event); break; } } else { /* We store the buffer to handle synchronization below */ context->lastbuf = GST_BUFFER (data); } } /* synchronize streams by always using the earliest buffer */ if (context->lastbuf) { if (!first_context) { first_context = context; } else { if (GST_BUFFER_TIMESTAMP (context->lastbuf) < GST_BUFFER_TIMESTAMP (first_context->lastbuf)) first_context = context; } } } /* If we handle no data at all, we're at the end-of-stream, so * we should signal EOS. */ if (!first_context) { gst_pad_push (filter->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS))); gst_element_set_eos (element); return; } /* So we do have data! Let's forward that to our source pad. */ gst_pad_push (filter->srcpad, GST_DATA (first_context->lastbuf)); first_context->lastbuf = NULL; } </PRE ><P > Note that a loop-function is allowed to return. Better yet, a loop function <SPAN CLASS="emphasis" ><I CLASS="emphasis" >has to</I ></SPAN > return so the scheduler can let other elements run (this is particularly true for the optimal scheduler). Whenever the scheduler feels right, it will call the loop-function of the element again. </P ></DIV ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="section-sched-opt.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="index.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="section-loopfn-bytestream.html" ACCESSKEY="N" >Next</A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >The Optimal Scheduler</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="part-advanced.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >The Bytestream Object</TD ></TR ></TABLE ></DIV ></BODY ></HTML >