+ g_main_loop_unref (loop);
+
+ return 0;
+ }
+]]>
+<!-- example-end appsrc.c -->
+ </programlisting>
+ </sect3>
+ </sect2>
+
+ <sect2 id="section-spoof-appsink">
+ <title>Grabbing data with appsink</title>
+ <para>
+ Unlike appsrc, appsink is a little easier to use. It also supports
+ a pull and push based model of getting data from the pipeline.
+ </para>
+ <para>
+ The normal way of retrieving samples from appsink is by using the
+ <function>gst_app_sink_pull_sample()</function> and
+ <function>gst_app_sink_pull_preroll()</function> methods or by using
+ the <quote>pull-sample</quote> and <quote>pull-preroll</quote>
+ signals. These methods block until a sample becomes available in the
+ sink or when the sink is shut down or reaches EOS.
+ </para>
+ <para>
+ Appsink will internally use a queue to collect buffers from the
+ streaming thread. If the application is not pulling samples fast
+ enough, this queue will consume a lot of memory over time. The
+ <quote>max-buffers</quote> property can be used to limit the queue
+ size. The <quote>drop</quote> property controls whether the
+ streaming thread blocks or if older buffers are dropped when the
+ maximum queue size is reached. Note that blocking the streaming thread
+ can negatively affect real-time performance and should be avoided.
+ </para>
+ <para>
+ If a blocking behaviour is not desirable, setting the
+ <quote>emit-signals</quote> property to TRUE will make appsink emit
+ the <quote>new-sample</quote> and <quote>new-preroll</quote> signals
+ when a sample can be pulled without blocking.
+ </para>
+ <para>
+ The <quote>caps</quote> property on appsink can be used to control
+ the formats that appsink can receive. This property can contain
+ non-fixed caps, the format of the pulled samples can be obtained by
+ getting the sample caps.
+ </para>
+ <para>
+ If one of the pull-preroll or pull-sample methods return NULL, the
+ appsink is stopped or in the EOS state. You can check for the EOS state
+ with the <quote>eos</quote> property or with the
+ <function>gst_app_sink_is_eos()</function> method.
+ </para>
+ <para>
+ The eos signal can also be used to be informed when the EOS state is
+ reached to avoid polling.
+ </para>
+ <para>
+ Consider configuring the following properties in the appsink:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ The <quote>sync</quote> property if you want to have the sink
+ base class synchronize the buffer against the pipeline clock
+ before handing you the sample.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Enable Quality-of-Service with the <quote>qos</quote> property.
+ If you are dealing with raw video frames and let the base class
+ sycnhronize on the clock, it might be a good idea to also let
+ the base class send QOS events upstream.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The caps property that contains the accepted caps. Upstream elements
+ will try to convert the format so that it matches the configured
+ caps on appsink. You must still check the
+ <classname>GstSample</classname> to get the actual caps of the
+ buffer.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <sect3 id="section-spoof-appsink-ex">
+ <title>Appsink example</title>
+ <para>
+ What follows is an example on how to capture a snapshot of a video
+ stream using appsink.
+ </para>
+ <programlisting>
+<!-- example-begin appsink.c -->
+<![CDATA[
+#include <gst/gst.h>
+#ifdef HAVE_GTK
+#include <gtk/gtk.h>
+#endif
+
+#include <stdlib.h>
+
+#define CAPS "video/x-raw,format=RGB,width=160,pixel-aspect-ratio=1/1"
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *sink;
+ gint width, height;
+ GstSample *sample;
+ gchar *descr;
+ GError *error = NULL;
+ gint64 duration, position;
+ GstStateChangeReturn ret;
+ gboolean res;
+ GstMapInfo map;
+
+ gst_init (&argc, &argv);
+
+ if (argc != 2) {
+ g_print ("usage: %s <uri>\n Writes snapshot.png in the current directory\n",
+ argv[0]);
+ exit (-1);
+ }
+
+ /* create a new pipeline */
+ descr =
+ g_strdup_printf ("uridecodebin uri=%s ! videoconvert ! videoscale ! "
+ " appsink name=sink caps=\"" CAPS "\"", argv[1]);
+ pipeline = gst_parse_launch (descr, &error);
+
+ if (error != NULL) {
+ g_print ("could not construct pipeline: %s\n", error->message);
+ g_clear_error (&error);
+ exit (-1);
+ }
+
+ /* get sink */
+ sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
+
+ /* set to PAUSED to make the first frame arrive in the sink */
+ ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ switch (ret) {
+ case GST_STATE_CHANGE_FAILURE:
+ g_print ("failed to play the file\n");
+ exit (-1);
+ case GST_STATE_CHANGE_NO_PREROLL:
+ /* for live sources, we need to set the pipeline to PLAYING before we can
+ * receive a buffer. We don't do that yet */
+ g_print ("live sources not supported yet\n");
+ exit (-1);
+ default:
+ break;
+ }
+ /* This can block for up to 5 seconds. If your machine is really overloaded,
+ * it might time out before the pipeline prerolled and we generate an error. A
+ * better way is to run a mainloop and catch errors there. */
+ ret = gst_element_get_state (pipeline, NULL, NULL, 5 * GST_SECOND);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ g_print ("failed to play the file\n");
+ exit (-1);
+ }
+
+ /* get the duration */
+ gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration);
+
+ if (duration != -1)
+ /* we have a duration, seek to 5% */
+ position = duration * 5 / 100;
+ else
+ /* no duration, seek to 1 second, this could EOS */
+ position = 1 * GST_SECOND;
+
+ /* seek to the a position in the file. Most files have a black first frame so
+ * by seeking to somewhere else we have a bigger chance of getting something
+ * more interesting. An optimisation would be to detect black images and then
+ * seek a little more */
+ gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH, position);
+
+ /* get the preroll buffer from appsink, this block untils appsink really
+ * prerolls */
+ g_signal_emit_by_name (sink, "pull-preroll", &sample, NULL);
+
+ /* if we have a buffer now, convert it to a pixbuf. It's possible that we
+ * don't have a buffer because we went EOS right away or had an error. */
+ if (sample) {
+ GstBuffer *buffer;
+ GstCaps *caps;
+ GstStructure *s;
+
+ /* get the snapshot buffer format now. We set the caps on the appsink so
+ * that it can only be an rgb buffer. The only thing we have not specified
+ * on the caps is the height, which is dependant on the pixel-aspect-ratio
+ * of the source material */
+ caps = gst_sample_get_caps (sample);
+ if (!caps) {
+ g_print ("could not get snapshot format\n");
+ exit (-1);
+ }
+ s = gst_caps_get_structure (caps, 0);
+
+ /* we need to get the final caps on the buffer to get the size */
+ res = gst_structure_get_int (s, "width", &width);
+ res |= gst_structure_get_int (s, "height", &height);
+ if (!res) {
+ g_print ("could not get snapshot dimension\n");
+ exit (-1);
+ }
+
+ /* create pixmap from buffer and save, gstreamer video buffers have a stride
+ * that is rounded up to the nearest multiple of 4 */
+ buffer = gst_sample_get_buffer (sample);
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+#ifdef HAVE_GTK
+ pixbuf = gdk_pixbuf_new_from_data (map.data,
+ GDK_COLORSPACE_RGB, FALSE, 8, width, height,
+ GST_ROUND_UP_4 (width * 3), NULL, NULL);
+
+ /* save the pixbuf */
+ gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
+#endif
+ gst_buffer_unmap (buffer, &map);
+ gst_sample_unref (sample);
+ } else {
+ g_print ("could not make snapshot\n");
+ }
+
+ /* cleanup and exit */
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ exit (0);
+}
+]]>
+<!-- example-end appsink.c -->
+</programlisting>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="section-spoof-format">
+ <title>Forcing a format</title>
+ <para>
+ Sometimes you'll want to set a specific format, for example a video
+ size and format or an audio bitsize and number of channels. You can
+ do this by forcing a specific <classname>GstCaps</classname> on
+ the pipeline, which is possible by using
+ <emphasis>filtered caps</emphasis>. You can set a filtered caps on
+ a link by using the <quote>capsfilter</quote> element in between the
+ two elements, and specifying a <classname>GstCaps</classname> as
+ <quote>caps</quote> property on this element. It will then
+ only allow types matching that specified capability set for
+ negotiation. See also <xref linkend="section-caps-filter"/>.
+ </para>
+
+ <sect2 id="section-dynamic-format">
+ <title>Changing format in a PLAYING pipeline</title>
+ <para>
+ It is also possible to dynamically change the format in a pipeline
+ while PLAYING. This can simply be done by changing the caps
+ property on a capsfilter. The capsfilter will send a RECONFIGURE
+ event upstream that will make the upstream element attempt to
+ renegotiate a new format and allocator. This only works if
+ the upstream element is not using fixed caps on the source pad.
+ </para>
+ <para>
+ Below is an example of how you can change the caps of a pipeline
+ while in the PLAYING state:
+ </para>
+ <programlisting>
+<!-- example-begin dynformat.c -->
+<![CDATA[
+#include <stdlib.h>
+
+#include <gst/gst.h>
+
+#define MAX_ROUND 100
+
+int
+main (int argc, char **argv)
+{
+ GstElement *pipe, *filter;
+ GstCaps *caps;
+ gint width, height;
+ gint xdir, ydir;
+ gint round;
+ GstMessage *message;
+
+ gst_init (&argc, &argv);
+
+ pipe = gst_parse_launch_full ("videotestsrc ! capsfilter name=filter ! "
+ "ximagesink", NULL, GST_PARSE_FLAG_NONE, NULL);
+ g_assert (pipe != NULL);
+
+ filter = gst_bin_get_by_name (GST_BIN (pipe), "filter");
+ g_assert (filter);
+
+ width = 320;
+ height = 240;
+ xdir = ydir = -10;
+
+ for (round = 0; round < MAX_ROUND; round++) {
+ gchar *capsstr;
+ g_print ("resize to %dx%d (%d/%d) \r", width, height, round, MAX_ROUND);
+
+ /* we prefer our fixed width and height but allow other dimensions to pass
+ * as well */
+ capsstr = g_strdup_printf ("video/x-raw, width=(int)%d, height=(int)%d",
+ width, height);
+
+ caps = gst_caps_from_string (capsstr);
+ g_free (capsstr);
+ g_object_set (filter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ if (round == 0)
+ gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+ width += xdir;
+ if (width >= 320)
+ xdir = -10;
+ else if (width < 200)
+ xdir = 10;
+
+ height += ydir;
+ if (height >= 240)
+ ydir = -10;
+ else if (height < 150)
+ ydir = 10;
+
+ message =
+ gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_ERROR,
+ 50 * GST_MSECOND);
+ if (message) {
+ g_print ("got error \n");
+
+ gst_message_unref (message);
+ }
+ }
+ g_print ("done \n");
+
+ gst_object_unref (filter);
+ gst_element_set_state (pipe, GST_STATE_NULL);
+ gst_object_unref (pipe);