From f31dcf4e71cdc1caece60cf761629878fae62700 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 2 Oct 2012 16:44:28 +0200 Subject: [PATCH] pwg: add appsink docs --- docs/manual/advanced-dataaccess.xml | 235 ++++++++++++++++++++++++++-- tests/examples/manual/Makefile.am | 5 + 2 files changed, 225 insertions(+), 15 deletions(-) diff --git a/docs/manual/advanced-dataaccess.xml b/docs/manual/advanced-dataaccess.xml index b8f6544ec6..ec04ed0e48 100644 --- a/docs/manual/advanced-dataaccess.xml +++ b/docs/manual/advanced-dataaccess.xml @@ -448,24 +448,229 @@ main (gint argc, Grabbing data with appsink - The two before-mentioned elements (fakesrc, fakesink and identity) - each have a handoff signal that will be called in - the _get ()- (fakesrc) or _chain - ()-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 sizemax - property. For both fakesrc and fakesink, you also have to set the - signal-handoffs 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. - Note that your handoff function should not - 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 + gst_app_sink_pull_sample() and + gst_app_sink_pull_preroll() methods or by using + the pull-sample and pull-preroll + signals. These methods block until a sample becomes available in the + sink or when the sink is shut down or reaches EOS. + + 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 + max-buffers property can be used to limit the queue + size. The drop 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. + + + If a blocking behaviour is not desirable, setting the + emit-signals property to TRUE will make appsink emit + the new-sample and new-preroll signals + when a sample can be pulled without blocking. + + + The caps 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. + + + 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 eos property or with the + gst_app_sink_is_eos() method. + + + The eos signal can also be used to be informed when the EOS state is + reached to avoid polling. + + + Consider configuring the following properties in the appsink: + + + + + The sync property if you want to have the sink + base class synchronize the buffer against the pipeline clock + before handing you the sample. + + + + + Enable Quality-of-Service with the qos 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. + + + + + 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 + GstSample to get the actual caps of the + buffer. + + + + + + Appsink example + + What follows is an example on how to capture a snapshot of a video + stream using appsink. + + + +#include +#ifdef HAVE_GTK +#include +#endif + +#include + +#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 \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); +} +]]> + + diff --git a/tests/examples/manual/Makefile.am b/tests/examples/manual/Makefile.am index 454c1fc69a..4876c7b5bb 100644 --- a/tests/examples/manual/Makefile.am +++ b/tests/examples/manual/Makefile.am @@ -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 $@ $<