mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
pwg: add appsink docs
This commit is contained in:
parent
cbc46176e0
commit
f31dcf4e71
2 changed files with 225 additions and 15 deletions
|
@ -448,24 +448,229 @@ main (gint argc,
|
||||||
<sect2 id="section-spoof-appsink">
|
<sect2 id="section-spoof-appsink">
|
||||||
<title>Grabbing data with appsink</title>
|
<title>Grabbing data with appsink</title>
|
||||||
<para>
|
<para>
|
||||||
The two before-mentioned elements (fakesrc, fakesink and identity)
|
Unlike appsrc, appsink is a little easier to use. It also supports
|
||||||
each have a <quote>handoff</quote> signal that will be called in
|
a pull and push based model of getting data from the pipeline.
|
||||||
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.
|
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Note that your handoff function should <emphasis>not</emphasis>
|
The normal way of retrieving samples from appsink is by using the
|
||||||
block, since this will block pipeline iteration. Also, do not try
|
<function>gst_app_sink_pull_sample()</function> and
|
||||||
to use all sort of weird hacks in such functions to accomplish
|
<function>gst_app_sink_pull_preroll()</function> methods or by using
|
||||||
something that looks like synchronization or so; it's not the right
|
the <quote>pull-sample</quote> and <quote>pull-preroll</quote>
|
||||||
way and will lead to issues elsewhere. If you're doing any of this,
|
signals. These methods block until a sample becomes available in the
|
||||||
you're basically misunderstanding the &GStreamer; design.
|
sink or when the sink is shut down or reaches EOS.
|
||||||
</para>
|
</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>
|
||||||
|
|
||||||
<sect2 id="section-spoof-format">
|
<sect2 id="section-spoof-format">
|
||||||
|
|
|
@ -37,6 +37,7 @@ EXAMPLES = \
|
||||||
typefind \
|
typefind \
|
||||||
probe \
|
probe \
|
||||||
appsrc \
|
appsrc \
|
||||||
|
appsink \
|
||||||
playbin \
|
playbin \
|
||||||
decodebin
|
decodebin
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ BUILT_SOURCES = \
|
||||||
typefind.c dynamic.c \
|
typefind.c dynamic.c \
|
||||||
probe.c \
|
probe.c \
|
||||||
appsrc.c \
|
appsrc.c \
|
||||||
|
appsink.c \
|
||||||
playbin.c decodebin.c
|
playbin.c decodebin.c
|
||||||
|
|
||||||
CLEANFILES = core core.* test-registry.* *.gcno *.gcda $(BUILT_SOURCES)
|
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
|
appsrc.c: $(top_srcdir)/docs/manual/advanced-dataaccess.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
$(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
|
playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml
|
||||||
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
$(PERL_PATH) $(srcdir)/extract.pl $@ $<
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue