pwg: add appsink docs
authorWim Taymans <wim.taymans@collabora.co.uk>
Tue, 2 Oct 2012 14:44:28 +0000 (16:44 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 2 Oct 2012 14:44:28 +0000 (16:44 +0200)
docs/manual/advanced-dataaccess.xml
tests/examples/manual/Makefile.am

index b8f6544..ec04ed0 100644 (file)
@@ -448,24 +448,229 @@ main (gint   argc,
     <sect2 id="section-spoof-appsink">
       <title>Grabbing data with appsink</title>
       <para>
-        The two before-mentioned elements (fakesrc, fakesink and identity)
-        each have a <quote>handoff</quote> signal that will be called in
-        the <function>_get ()</function>- (fakesrc) or <function>_chain
-        ()</function>-function (identity, fakesink). In the signal handler,
-        you can set (fakesrc) or get (identity, fakesink) data to/from the
-        provided buffer. Note that in the case of fakesrc, you have to set
-        the size of the provided buffer using the <quote>sizemax</quote>
-        property. For both fakesrc and fakesink, you also have to set the
-        <quote>signal-handoffs</quote> property for this method to work.
+        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>
-        Note that your handoff function should <emphasis>not</emphasis>
-        block, since this will block pipeline iteration. Also, do not try
-        to use all sort of weird hacks in such functions to accomplish
-        something that looks like synchronization or so; it's not the right
-        way and will lead to issues elsewhere. If you're doing any of this,
-        you're basically misunderstanding the &GStreamer; design.
+        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>
+<![CDATA[
+<!-- example-begin appsink.c -->
+#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_error_free (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);
+  } 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>
 
     <sect2 id="section-spoof-format">
index 454c1fc..4876c7b 100644 (file)
@@ -37,6 +37,7 @@ EXAMPLES = \
        typefind \
        probe \
        appsrc \
+       appsink \
        playbin \
        decodebin
 
@@ -51,6 +52,7 @@ BUILT_SOURCES = \
        typefind.c dynamic.c \
        probe.c \
        appsrc.c \
+       appsink.c \
        playbin.c decodebin.c
 
 CLEANFILES = core core.* test-registry.* *.gcno *.gcda $(BUILT_SOURCES)
@@ -89,6 +91,9 @@ probe.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
 appsrc.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
        $(PERL_PATH) $(srcdir)/extract.pl $@ $<
 
+appsink.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
+       $(PERL_PATH) $(srcdir)/extract.pl $@ $<
+
 playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml
        $(PERL_PATH) $(srcdir)/extract.pl $@ $<