1 <chapter id="chapter-queryevents">
2 <title>Position tracking and seeking</title>
5 So far, we've looked at how to create a pipeline to do media processing
6 and how to make it run. Most application developers will be interested
7 in providing feedback to the user on media progress. Media players, for
8 example, will want to show a slider showing the progress in the song,
9 and usually also a label indicating stream length. Transcoding
10 applications will want to show a progress bar on how much percent of
11 the task is done. &GStreamer; has built-in support for doing all this
12 using a concept known as <emphasis>querying</emphasis>. Since seeking
13 is very similar, it will be discussed here as well. Seeking is done
14 using the concept of <emphasis>events</emphasis>.
17 <sect1 id="section-querying">
18 <title>Querying: getting the position or length of a stream</title>
21 Querying is defined as requesting a specific stream property related
22 to progress tracking. This includes getting the length of a stream (if
23 available) or getting the current position. Those stream properties
24 can be retrieved in various formats such as time, audio samples, video
25 frames or bytes. The function most commonly used for this is
26 <function>gst_element_query ()</function>, although some convenience
27 wrappers are provided as well (such as
28 <function>gst_element_query_position ()</function> and
29 <function>gst_element_query_duration ()</function>). You can generally
30 query the pipeline directly, and it'll figure out the internal details
31 for you, like which element to query.
35 Internally, queries will be sent to the sinks, and
36 <quote>dispatched</quote> backwards until one element can handle it;
37 that result will be sent back to the function caller. Usually, that
38 is the demuxer, although with live sources (from a webcam), it is the
43 <!-- example-begin query.c a -->
44 #include <gst/gst.h>
45 <!-- example-end query.c a -->
46 <!-- example-begin query.c b --><!--
48 my_bus_message_cb (GstBus *bus,
52 GMainLoop *loop = (GMainLoop *) data;
54 switch (GST_MESSAGE_TYPE (message)) {
55 case GST_MESSAGE_ERROR: {
59 gst_message_parse_error (message, &err, &debug);
60 g_print ("Error: %s\n", err->message);
64 g_main_loop_quit (loop);
69 g_main_loop_quit (loop);
76 <!-- example-end query.c b -->
77 <!-- example-begin query.c c -->
79 cb_print_position (GstElement *pipeline)
83 if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos)
84 && gst_element_query_duration (pipeline, GST_FORMAT_TIME, &len)) {
85 g_print ("Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r",
86 GST_TIME_ARGS (pos), GST_TIME_ARGS (len));
98 <!-- example-end query.c c -->
99 [..]<!-- example-begin query.c d --><!--
100 GstStateChangeReturn ret;
107 gst_init (&argc, &argv);
111 g_print ("Usage: %s <filename>\n", argv[0]);
115 loop = g_main_loop_new (NULL, FALSE);
117 /* build pipeline, the easy way */
118 l = g_strdup_printf ("filesrc location=\"%s\" ! oggdemux ! vorbisdec ! "
119 "audioconvert ! audioresample ! alsasink",
121 pipeline = gst_parse_launch (l, &err);
122 if (pipeline == NULL || err != NULL) {
123 g_printerr ("Cannot build pipeline: %s\n", err->message);
127 gst_object_unref (pipeline);
132 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
133 gst_bus_add_signal_watch (bus);
134 g_signal_connect (bus, "message", G_CALLBACK (my_bus_message_cb), loop);
135 gst_object_unref (bus);
138 ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
139 if (ret == GST_STATE_CHANGE_FAILURE)
140 g_error ("Failed to set pipeline to PLAYING.\n");
141 --><!-- example-end query.c d -->
142 <!-- example-begin query.c e -->
144 g_timeout_add (200, (GSourceFunc) cb_print_position, pipeline);
145 g_main_loop_run (loop);
146 <!-- example-end query.c e -->
147 [..]<!-- example-begin query.c f --><!--
149 gst_element_set_state (pipeline, GST_STATE_NULL);
150 gst_object_unref (GST_OBJECT (pipeline));
153 --><!-- example-end query.c f -->
154 <!-- example-begin query.c g -->
156 <!-- example-end query.c g --></programlisting>
159 <sect1 id="section-eventsseek">
160 <title>Events: seeking (and more)</title>
163 Events work in a very similar way as queries. Dispatching, for
164 example, works exactly the same for events (and also has the same
165 limitations), and they can similarly be sent to the toplevel pipeline
166 and it will figure out everything for you. Although there are more
167 ways in which applications and elements can interact using events,
168 we will only focus on seeking here. This is done using the seek-event.
169 A seek-event contains a playback rate, a seek offset format (which is
170 the unit of the offsets to follow, e.g. time, audio samples, video
171 frames or bytes), optionally a set of seeking-related flags (e.g.
172 whether internal buffers should be flushed), a seek method (which
173 indicates relative to what the offset was given), and seek offsets.
174 The first offset (cur) is the new position to seek to, while
175 the second offset (stop) is optional and specifies a position where
176 streaming is supposed to stop. Usually it is fine to just specify
177 GST_SEEK_TYPE_NONE and -1 as end_method and end offset. The behaviour
178 of a seek is also wrapped in the <function>gst_element_seek ()</function>.
183 seek_to_time (GstElement *pipeline,
184 gint64 time_nanoseconds)
186 if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
187 GST_SEEK_TYPE_SET, time_nanoseconds,
188 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
189 g_print ("Seek failed!\n");
194 Seeks with the GST_SEEK_FLAG_FLUSH should be done when the pipeline is
195 in PAUSED or PLAYING state. The pipeline will automatically go to preroll
196 state until the new data after the seek will cause the pipeline to preroll
197 again. After the pipeline is prerolled, it will go back to the state
198 (PAUSED or PLAYING) it was in when the seek was executed. You can wait
199 (blocking) for the seek to complete with
200 <function>gst_element_get_state()</function> or by waiting for the
201 ASYNC_DONE message to appear on the bus.
205 Seeks without the GST_SEEK_FLAG_FLUSH should only be done when the
206 pipeline is in the PLAYING state. Executing a non-flushing seek in the
207 PAUSED state might deadlock because the pipeline streaming threads might
208 be blocked in the sinks.
212 It is important to realise that seeks will not happen instantly in the
213 sense that they are finished when the function
214 <function>gst_element_seek ()</function> returns. Depending on the
215 specific elements involved, the actual seeking might be done later in
216 another thread (the streaming thread), and it might take a short time
217 until buffers from the new seek position will reach downstream elements
218 such as sinks (if the seek was non-flushing then it might take a bit
223 It is possible to do multiple seeks in short time-intervals, such as
224 a direct response to slider movement. After a seek, internally, the
225 pipeline will be paused (if it was playing), the position will be
226 re-set internally, the demuxers and decoders will decode from the new
227 position onwards and this will continue until all sinks have data
228 again. If it was playing originally, it will be set to playing again,
229 too. Since the new position is immediately available in a video output,
230 you will see the new frame, even if your pipeline is not in the playing