Branch data Line data Source code
1 : : /* GStreamer
2 : : * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com>
3 : : *
4 : : * gstbasesink.c: Base class for sink elements
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Library General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Library General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Library General Public
17 : : * License along with this library; if not, write to the
18 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 : : * Boston, MA 02111-1307, USA.
20 : : */
21 : :
22 : : /**
23 : : * SECTION:gstbasesink
24 : : * @short_description: Base class for sink elements
25 : : * @see_also: #GstBaseTransform, #GstBaseSrc
26 : : *
27 : : * #GstBaseSink is the base class for sink elements in GStreamer, such as
28 : : * xvimagesink or filesink. It is a layer on top of #GstElement that provides a
29 : : * simplified interface to plugin writers. #GstBaseSink handles many details
30 : : * for you, for example: preroll, clock synchronization, state changes,
31 : : * activation in push or pull mode, and queries.
32 : : *
33 : : * In most cases, when writing sink elements, there is no need to implement
34 : : * class methods from #GstElement or to set functions on pads, because the
35 : : * #GstBaseSink infrastructure should be sufficient.
36 : : *
37 : : * #GstBaseSink provides support for exactly one sink pad, which should be
38 : : * named "sink". A sink implementation (subclass of #GstBaseSink) should
39 : : * install a pad template in its base_init function, like so:
40 : : * |[
41 : : * static void
42 : : * my_element_base_init (gpointer g_class)
43 : : * {
44 : : * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
45 : : *
46 : : * // sinktemplate should be a #GstStaticPadTemplate with direction
47 : : * // #GST_PAD_SINK and name "sink"
48 : : * gst_element_class_add_pad_template (gstelement_class,
49 : : * gst_static_pad_template_get (&sinktemplate));
50 : : * // see #GstElementDetails
51 : : * gst_element_class_set_details (gstelement_class, &details);
52 : : * }
53 : : * ]|
54 : : *
55 : : * #GstBaseSink will handle the prerolling correctly. This means that it will
56 : : * return #GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
57 : : * buffer arrives in this element. The base class will call the
58 : : * #GstBaseSinkClass.preroll() vmethod with this preroll buffer and will then
59 : : * commit the state change to the next asynchronously pending state.
60 : : *
61 : : * When the element is set to PLAYING, #GstBaseSink will synchronise on the
62 : : * clock using the times returned from #GstBaseSinkClass.get_times(). If this
63 : : * function returns #GST_CLOCK_TIME_NONE for the start time, no synchronisation
64 : : * will be done. Synchronisation can be disabled entirely by setting the object
65 : : * #GstBaseSink:sync property to %FALSE.
66 : : *
67 : : * After synchronisation the virtual method #GstBaseSinkClass.render() will be
68 : : * called. Subclasses should minimally implement this method.
69 : : *
70 : : * Since 0.10.3 subclasses that synchronise on the clock in the
71 : : * #GstBaseSinkClass.render() method are supported as well. These classes
72 : : * typically receive a buffer in the render method and can then potentially
73 : : * block on the clock while rendering. A typical example is an audiosink.
74 : : * Since 0.10.11 these subclasses can use gst_base_sink_wait_preroll() to
75 : : * perform the blocking wait.
76 : : *
77 : : * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait
78 : : * for the clock to reach the time indicated by the stop time of the last
79 : : * #GstBaseSinkClass.get_times() call before posting an EOS message. When the
80 : : * element receives EOS in PAUSED, preroll completes, the event is queued and an
81 : : * EOS message is posted when going to PLAYING.
82 : : *
83 : : * #GstBaseSink will internally use the #GST_EVENT_NEWSEGMENT events to schedule
84 : : * synchronisation and clipping of buffers. Buffers that fall completely outside
85 : : * of the current segment are dropped. Buffers that fall partially in the
86 : : * segment are rendered (and prerolled). Subclasses should do any subbuffer
87 : : * clipping themselves when needed.
88 : : *
89 : : * #GstBaseSink will by default report the current playback position in
90 : : * #GST_FORMAT_TIME based on the current clock time and segment information.
91 : : * If no clock has been set on the element, the query will be forwarded
92 : : * upstream.
93 : : *
94 : : * The #GstBaseSinkClass.set_caps() function will be called when the subclass
95 : : * should configure itself to process a specific media type.
96 : : *
97 : : * The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
98 : : * will be called when resources should be allocated. Any
99 : : * #GstBaseSinkClass.preroll(), #GstBaseSinkClass.render() and
100 : : * #GstBaseSinkClass.set_caps() function will be called between the
101 : : * #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() calls.
102 : : *
103 : : * The #GstBaseSinkClass.event() virtual method will be called when an event is
104 : : * received by #GstBaseSink. Normally this method should only be overriden by
105 : : * very specific elements (such as file sinks) which need to handle the
106 : : * newsegment event specially.
107 : : *
108 : : * #GstBaseSink provides an overridable #GstBaseSinkClass.buffer_alloc()
109 : : * function that can be used by sinks that want to do reverse negotiation or to
110 : : * provide custom buffers (hardware buffers for example) to upstream elements.
111 : : *
112 : : * The #GstBaseSinkClass.unlock() method is called when the elements should
113 : : * unblock any blocking operations they perform in the
114 : : * #GstBaseSinkClass.render() method. This is mostly useful when the
115 : : * #GstBaseSinkClass.render() method performs a blocking write on a file
116 : : * descriptor, for example.
117 : : *
118 : : * The #GstBaseSink:max-lateness property affects how the sink deals with
119 : : * buffers that arrive too late in the sink. A buffer arrives too late in the
120 : : * sink when the presentation time (as a combination of the last segment, buffer
121 : : * timestamp and element base_time) plus the duration is before the current
122 : : * time of the clock.
123 : : * If the frame is later than max-lateness, the sink will drop the buffer
124 : : * without calling the render method.
125 : : * This feature is disabled if sync is disabled, the
126 : : * #GstBaseSinkClass.get_times() method does not return a valid start time or
127 : : * max-lateness is set to -1 (the default).
128 : : * Subclasses can use gst_base_sink_set_max_lateness() to configure the
129 : : * max-lateness value.
130 : : *
131 : : * The #GstBaseSink:qos property will enable the quality-of-service features of
132 : : * the basesink which gather statistics about the real-time performance of the
133 : : * clock synchronisation. For each buffer received in the sink, statistics are
134 : : * gathered and a QOS event is sent upstream with these numbers. This
135 : : * information can then be used by upstream elements to reduce their processing
136 : : * rate, for example.
137 : : *
138 : : * Since 0.10.15 the #GstBaseSink:async property can be used to instruct the
139 : : * sink to never perform an ASYNC state change. This feature is mostly usable
140 : : * when dealing with non-synchronized streams or sparse streams.
141 : : *
142 : : * Last reviewed on 2007-08-29 (0.10.15)
143 : : */
144 : :
145 : : #ifdef HAVE_CONFIG_H
146 : : # include "config.h"
147 : : #endif
148 : :
149 : : #include <gst/gst_private.h>
150 : :
151 : : #include "gstbasesink.h"
152 : : #include <gst/gstmarshal.h>
153 : : #include <gst/gst-i18n-lib.h>
154 : :
155 : : GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
156 : : #define GST_CAT_DEFAULT gst_base_sink_debug
157 : :
158 : : #define GST_BASE_SINK_GET_PRIVATE(obj) \
159 : : (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
160 : :
161 : : #define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
162 : :
163 : : typedef struct
164 : : {
165 : : gboolean valid; /* if this info is valid */
166 : : guint32 seqnum; /* the seqnum of the STEP event */
167 : : GstFormat format; /* the format of the amount */
168 : : guint64 amount; /* the total amount of data to skip */
169 : : guint64 position; /* the position in the stepped data */
170 : : guint64 duration; /* the duration in time of the skipped data */
171 : : guint64 start; /* running_time of the start */
172 : : gdouble rate; /* rate of skipping */
173 : : gdouble start_rate; /* rate before skipping */
174 : : guint64 start_start; /* start position skipping */
175 : : guint64 start_stop; /* stop position skipping */
176 : : gboolean flush; /* if this was a flushing step */
177 : : gboolean intermediate; /* if this is an intermediate step */
178 : : gboolean need_preroll; /* if we need preroll after this step */
179 : : } GstStepInfo;
180 : :
181 : : /* FIXME, some stuff in ABI.data and other in Private...
182 : : * Make up your mind please.
183 : : */
184 : : struct _GstBaseSinkPrivate
185 : : {
186 : : gint qos_enabled; /* ATOMIC */
187 : : gboolean async_enabled;
188 : : GstClockTimeDiff ts_offset;
189 : : GstClockTime render_delay;
190 : :
191 : : /* start, stop of current buffer, stream time, used to report position */
192 : : GstClockTime current_sstart;
193 : : GstClockTime current_sstop;
194 : :
195 : : /* start, stop and jitter of current buffer, running time */
196 : : GstClockTime current_rstart;
197 : : GstClockTime current_rstop;
198 : : GstClockTimeDiff current_jitter;
199 : : /* the running time of the previous buffer */
200 : : GstClockTime prev_rstart;
201 : :
202 : : /* EOS sync time in running time */
203 : : GstClockTime eos_rtime;
204 : :
205 : : /* last buffer that arrived in time, running time */
206 : : GstClockTime last_render_time;
207 : : /* when the last buffer left the sink, running time */
208 : : GstClockTime last_left;
209 : :
210 : : /* running averages go here these are done on running time */
211 : : GstClockTime avg_pt;
212 : : GstClockTime avg_duration;
213 : : gdouble avg_rate;
214 : : GstClockTime avg_in_diff;
215 : :
216 : : /* these are done on system time. avg_jitter and avg_render are
217 : : * compared to eachother to see if the rendering time takes a
218 : : * huge amount of the processing, If so we are flooded with
219 : : * buffers. */
220 : : GstClockTime last_left_systime;
221 : : GstClockTime avg_jitter;
222 : : GstClockTime start, stop;
223 : : GstClockTime avg_render;
224 : :
225 : : /* number of rendered and dropped frames */
226 : : guint64 rendered;
227 : : guint64 dropped;
228 : :
229 : : /* latency stuff */
230 : : GstClockTime latency;
231 : :
232 : : /* if we already commited the state */
233 : : gboolean commited;
234 : :
235 : : /* when we received EOS */
236 : : gboolean received_eos;
237 : :
238 : : /* when we are prerolled and able to report latency */
239 : : gboolean have_latency;
240 : :
241 : : /* the last buffer we prerolled or rendered. Useful for making snapshots */
242 : : gint enable_last_buffer; /* atomic */
243 : : GstBuffer *last_buffer;
244 : :
245 : : /* caps for pull based scheduling */
246 : : GstCaps *pull_caps;
247 : :
248 : : /* blocksize for pulling */
249 : : guint blocksize;
250 : :
251 : : gboolean discont;
252 : :
253 : : /* seqnum of the stream */
254 : : guint32 seqnum;
255 : :
256 : : gboolean call_preroll;
257 : : gboolean step_unlock;
258 : :
259 : : /* we have a pending and a current step operation */
260 : : GstStepInfo current_step;
261 : : GstStepInfo pending_step;
262 : :
263 : : /* Cached GstClockID */
264 : : GstClockID cached_clock_id;
265 : :
266 : : /* for throttling and QoS */
267 : : GstClockTime earliest_in_time;
268 : : GstClockTime throttle_time;
269 : : };
270 : :
271 : : #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
272 : :
273 : : /* generic running average, this has a neutral window size */
274 : : #define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
275 : :
276 : : /* the windows for these running averages are experimentally obtained.
277 : : * possitive values get averaged more while negative values use a small
278 : : * window so we can react faster to badness. */
279 : : #define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
280 : : #define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
281 : :
282 : : enum
283 : : {
284 : : _PR_IS_NOTHING = 1 << 0,
285 : : _PR_IS_BUFFER = 1 << 1,
286 : : _PR_IS_BUFFERLIST = 1 << 2,
287 : : _PR_IS_EVENT = 1 << 3
288 : : } PrivateObjectType;
289 : :
290 : : #define OBJ_IS_BUFFER(a) ((a) & _PR_IS_BUFFER)
291 : : #define OBJ_IS_BUFFERLIST(a) ((a) & _PR_IS_BUFFERLIST)
292 : : #define OBJ_IS_EVENT(a) ((a) & _PR_IS_EVENT)
293 : : #define OBJ_IS_BUFFERFULL(a) ((a) & (_PR_IS_BUFFER | _PR_IS_BUFFERLIST))
294 : :
295 : : /* BaseSink properties */
296 : :
297 : : #define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
298 : : #define DEFAULT_CAN_ACTIVATE_PUSH TRUE
299 : :
300 : : #define DEFAULT_PREROLL_QUEUE_LEN 0
301 : : #define DEFAULT_SYNC TRUE
302 : : #define DEFAULT_MAX_LATENESS -1
303 : : #define DEFAULT_QOS FALSE
304 : : #define DEFAULT_ASYNC TRUE
305 : : #define DEFAULT_TS_OFFSET 0
306 : : #define DEFAULT_BLOCKSIZE 4096
307 : : #define DEFAULT_RENDER_DELAY 0
308 : : #define DEFAULT_ENABLE_LAST_BUFFER TRUE
309 : : #define DEFAULT_THROTTLE_TIME 0
310 : :
311 : : enum
312 : : {
313 : : PROP_0,
314 : : PROP_PREROLL_QUEUE_LEN,
315 : : PROP_SYNC,
316 : : PROP_MAX_LATENESS,
317 : : PROP_QOS,
318 : : PROP_ASYNC,
319 : : PROP_TS_OFFSET,
320 : : PROP_ENABLE_LAST_BUFFER,
321 : : PROP_LAST_BUFFER,
322 : : PROP_BLOCKSIZE,
323 : : PROP_RENDER_DELAY,
324 : : PROP_THROTTLE_TIME,
325 : : PROP_LAST
326 : : };
327 : :
328 : : static GstElementClass *parent_class = NULL;
329 : :
330 : : static void gst_base_sink_class_init (GstBaseSinkClass * klass);
331 : : static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);
332 : : static void gst_base_sink_finalize (GObject * object);
333 : :
334 : : GType
335 : 985452 : gst_base_sink_get_type (void)
336 : : {
337 : : static volatile gsize base_sink_type = 0;
338 : :
339 [ + + ]: 985452 : if (g_once_init_enter (&base_sink_type)) {
340 : : GType _type;
341 : : static const GTypeInfo base_sink_info = {
342 : : sizeof (GstBaseSinkClass),
343 : : NULL,
344 : : NULL,
345 : : (GClassInitFunc) gst_base_sink_class_init,
346 : : NULL,
347 : : NULL,
348 : : sizeof (GstBaseSink),
349 : : 0,
350 : : (GInstanceInitFunc) gst_base_sink_init,
351 : : };
352 : :
353 : 153 : _type = g_type_register_static (GST_TYPE_ELEMENT,
354 : : "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
355 : 153 : g_once_init_leave (&base_sink_type, _type);
356 : : }
357 : 985700 : return base_sink_type;
358 : : }
359 : :
360 : : static void gst_base_sink_set_property (GObject * object, guint prop_id,
361 : : const GValue * value, GParamSpec * pspec);
362 : : static void gst_base_sink_get_property (GObject * object, guint prop_id,
363 : : GValue * value, GParamSpec * pspec);
364 : :
365 : : static gboolean gst_base_sink_send_event (GstElement * element,
366 : : GstEvent * event);
367 : : static gboolean gst_base_sink_query (GstElement * element, GstQuery * query);
368 : : static const GstQueryType *gst_base_sink_get_query_types (GstElement * element);
369 : :
370 : : static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
371 : : static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
372 : : static GstFlowReturn gst_base_sink_buffer_alloc (GstBaseSink * sink,
373 : : guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
374 : : static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
375 : : GstClockTime * start, GstClockTime * end);
376 : : static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
377 : : GstPad * pad, gboolean flushing);
378 : : static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
379 : : gboolean active);
380 : : static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink,
381 : : GstSegment * segment);
382 : : static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
383 : : GstEvent * event, GstSegment * segment);
384 : :
385 : : static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
386 : : GstStateChange transition);
387 : :
388 : : static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
389 : : static GstFlowReturn gst_base_sink_chain_list (GstPad * pad,
390 : : GstBufferList * list);
391 : :
392 : : static void gst_base_sink_loop (GstPad * pad);
393 : : static gboolean gst_base_sink_pad_activate (GstPad * pad);
394 : : static gboolean gst_base_sink_pad_activate_push (GstPad * pad, gboolean active);
395 : : static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);
396 : : static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
397 : :
398 : : static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
399 : : static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad);
400 : : static gboolean gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps);
401 : : static void gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps);
402 : : static GstFlowReturn gst_base_sink_pad_buffer_alloc (GstPad * pad,
403 : : guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
404 : :
405 : :
406 : : /* check if an object was too late */
407 : : static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
408 : : GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop,
409 : : GstClockReturn status, GstClockTimeDiff jitter);
410 : : static GstFlowReturn gst_base_sink_preroll_object (GstBaseSink * basesink,
411 : : guint8 obj_type, GstMiniObject * obj);
412 : :
413 : : static void
414 : 95 : gst_base_sink_class_init (GstBaseSinkClass * klass)
415 : : {
416 : : GObjectClass *gobject_class;
417 : : GstElementClass *gstelement_class;
418 : :
419 : 95 : gobject_class = G_OBJECT_CLASS (klass);
420 : 95 : gstelement_class = GST_ELEMENT_CLASS (klass);
421 : :
422 [ + - ]: 95 : GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
423 : : "basesink element");
424 : :
425 : 95 : g_type_class_add_private (klass, sizeof (GstBaseSinkPrivate));
426 : :
427 : 95 : parent_class = g_type_class_peek_parent (klass);
428 : :
429 : 95 : gobject_class->finalize = gst_base_sink_finalize;
430 : 95 : gobject_class->set_property = gst_base_sink_set_property;
431 : 95 : gobject_class->get_property = gst_base_sink_get_property;
432 : :
433 : : /* FIXME, this next value should be configured using an event from the
434 : : * upstream element, ie, the BUFFER_SIZE event. */
435 : 95 : g_object_class_install_property (gobject_class, PROP_PREROLL_QUEUE_LEN,
436 : : g_param_spec_uint ("preroll-queue-len", "Preroll queue length",
437 : : "Number of buffers to queue during preroll", 0, G_MAXUINT,
438 : : DEFAULT_PREROLL_QUEUE_LEN,
439 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
440 : :
441 : 95 : g_object_class_install_property (gobject_class, PROP_SYNC,
442 : : g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
443 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
444 : :
445 : 95 : g_object_class_install_property (gobject_class, PROP_MAX_LATENESS,
446 : : g_param_spec_int64 ("max-lateness", "Max Lateness",
447 : : "Maximum number of nanoseconds that a buffer can be late before it "
448 : : "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS,
449 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 : :
451 : 95 : g_object_class_install_property (gobject_class, PROP_QOS,
452 : : g_param_spec_boolean ("qos", "Qos",
453 : : "Generate Quality-of-Service events upstream", DEFAULT_QOS,
454 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 : : /**
456 : : * GstBaseSink:async
457 : : *
458 : : * If set to #TRUE, the basesink will perform asynchronous state changes.
459 : : * When set to #FALSE, the sink will not signal the parent when it prerolls.
460 : : * Use this option when dealing with sparse streams or when synchronisation is
461 : : * not required.
462 : : *
463 : : * Since: 0.10.15
464 : : */
465 : 95 : g_object_class_install_property (gobject_class, PROP_ASYNC,
466 : : g_param_spec_boolean ("async", "Async",
467 : : "Go asynchronously to PAUSED", DEFAULT_ASYNC,
468 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469 : : /**
470 : : * GstBaseSink:ts-offset
471 : : *
472 : : * Controls the final synchronisation, a negative value will render the buffer
473 : : * earlier while a positive value delays playback. This property can be
474 : : * used to fix synchronisation in bad files.
475 : : *
476 : : * Since: 0.10.15
477 : : */
478 : 95 : g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
479 : : g_param_spec_int64 ("ts-offset", "TS Offset",
480 : : "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64,
481 : : DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482 : :
483 : : /**
484 : : * GstBaseSink:enable-last-buffer
485 : : *
486 : : * Enable the last-buffer property. If FALSE, basesink doesn't keep a
487 : : * reference to the last buffer arrived and the last-buffer property is always
488 : : * set to NULL. This can be useful if you need buffers to be released as soon
489 : : * as possible, eg. if you're using a buffer pool.
490 : : *
491 : : * Since: 0.10.30
492 : : */
493 : 95 : g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_BUFFER,
494 : : g_param_spec_boolean ("enable-last-buffer", "Enable Last Buffer",
495 : : "Enable the last-buffer property", DEFAULT_ENABLE_LAST_BUFFER,
496 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
497 : :
498 : : /**
499 : : * GstBaseSink:last-buffer
500 : : *
501 : : * The last buffer that arrived in the sink and was used for preroll or for
502 : : * rendering. This property can be used to generate thumbnails. This property
503 : : * can be NULL when the sink has not yet received a bufer.
504 : : *
505 : : * Since: 0.10.15
506 : : */
507 : 95 : g_object_class_install_property (gobject_class, PROP_LAST_BUFFER,
508 : : gst_param_spec_mini_object ("last-buffer", "Last Buffer",
509 : : "The last buffer received in the sink", GST_TYPE_BUFFER,
510 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
511 : : /**
512 : : * GstBaseSink:blocksize
513 : : *
514 : : * The amount of bytes to pull when operating in pull mode.
515 : : *
516 : : * Since: 0.10.22
517 : : */
518 : : /* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
519 : 95 : g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
520 : : g_param_spec_uint ("blocksize", "Block size",
521 : : "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
522 : : DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523 : : /**
524 : : * GstBaseSink:render-delay
525 : : *
526 : : * The additional delay between synchronisation and actual rendering of the
527 : : * media. This property will add additional latency to the device in order to
528 : : * make other sinks compensate for the delay.
529 : : *
530 : : * Since: 0.10.22
531 : : */
532 : 95 : g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
533 : : g_param_spec_uint64 ("render-delay", "Render Delay",
534 : : "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64,
535 : : DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
536 : : /**
537 : : * GstBaseSink:throttle-time
538 : : *
539 : : * The time to insert between buffers. This property can be used to control
540 : : * the maximum amount of buffers per second to render. Setting this property
541 : : * to a value bigger than 0 will make the sink create THROTTLE QoS events.
542 : : *
543 : : * Since: 0.10.33
544 : : */
545 : 95 : g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
546 : : g_param_spec_uint64 ("throttle-time", "Throttle time",
547 : : "The time to keep between rendered buffers (unused)", 0, G_MAXUINT64,
548 : : DEFAULT_THROTTLE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
549 : :
550 : 95 : gstelement_class->change_state =
551 : 95 : GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
552 : 95 : gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
553 : 95 : gstelement_class->query = GST_DEBUG_FUNCPTR (gst_base_sink_query);
554 : 95 : gstelement_class->get_query_types =
555 : 95 : GST_DEBUG_FUNCPTR (gst_base_sink_get_query_types);
556 : :
557 : 95 : klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
558 : 95 : klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
559 : 95 : klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc);
560 : 95 : klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
561 : 95 : klass->activate_pull =
562 : 95 : GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
563 : :
564 : : /* Registering debug symbols for function pointers */
565 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_getcaps);
566 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_setcaps);
567 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_fixate);
568 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_buffer_alloc);
569 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate);
570 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_push);
571 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_pull);
572 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event);
573 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain);
574 : 95 : GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list);
575 : 95 : }
576 : :
577 : : static GstCaps *
578 : 260 : gst_base_sink_pad_getcaps (GstPad * pad)
579 : : {
580 : : GstBaseSinkClass *bclass;
581 : : GstBaseSink *bsink;
582 : 260 : GstCaps *caps = NULL;
583 : :
584 : 260 : bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
585 : 260 : bclass = GST_BASE_SINK_GET_CLASS (bsink);
586 : :
587 [ + + ]: 260 : if (bsink->pad_mode == GST_ACTIVATE_PULL) {
588 : : /* if we are operating in pull mode we only accept the negotiated caps */
589 : 6 : GST_OBJECT_LOCK (pad);
590 [ - + ]: 6 : if ((caps = GST_PAD_CAPS (pad)))
591 : 0 : gst_caps_ref (caps);
592 : 6 : GST_OBJECT_UNLOCK (pad);
593 : : }
594 [ + - ]: 260 : if (caps == NULL) {
595 [ + - ]: 260 : if (bclass->get_caps)
596 : 260 : caps = bclass->get_caps (bsink);
597 : :
598 [ + - ]: 260 : if (caps == NULL) {
599 : : GstPadTemplate *pad_template;
600 : :
601 : 260 : pad_template =
602 : 260 : gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass),
603 : : "sink");
604 [ + - ]: 260 : if (pad_template != NULL) {
605 : 260 : caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
606 : : }
607 : : }
608 : : }
609 : 260 : gst_object_unref (bsink);
610 : :
611 : 260 : return caps;
612 : : }
613 : :
614 : : static gboolean
615 : 4 : gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps)
616 : : {
617 : : GstBaseSinkClass *bclass;
618 : : GstBaseSink *bsink;
619 : 4 : gboolean res = TRUE;
620 : :
621 : 4 : bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
622 : 4 : bclass = GST_BASE_SINK_GET_CLASS (bsink);
623 : :
624 [ + - ][ + - ]: 4 : if (res && bclass->set_caps)
625 : 4 : res = bclass->set_caps (bsink, caps);
626 : :
627 : 4 : gst_object_unref (bsink);
628 : :
629 : 4 : return res;
630 : : }
631 : :
632 : : static void
633 : 0 : gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps)
634 : : {
635 : : GstBaseSinkClass *bclass;
636 : : GstBaseSink *bsink;
637 : :
638 : 0 : bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
639 : 0 : bclass = GST_BASE_SINK_GET_CLASS (bsink);
640 : :
641 [ # # ]: 0 : if (bclass->fixate)
642 : 0 : bclass->fixate (bsink, caps);
643 : :
644 : 0 : gst_object_unref (bsink);
645 : 0 : }
646 : :
647 : : static GstFlowReturn
648 : 0 : gst_base_sink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
649 : : GstCaps * caps, GstBuffer ** buf)
650 : : {
651 : : GstBaseSinkClass *bclass;
652 : : GstBaseSink *bsink;
653 : 0 : GstFlowReturn result = GST_FLOW_OK;
654 : :
655 : 0 : bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
656 : 0 : bclass = GST_BASE_SINK_GET_CLASS (bsink);
657 : :
658 [ # # ]: 0 : if (bclass->buffer_alloc)
659 : 0 : result = bclass->buffer_alloc (bsink, offset, size, caps, buf);
660 : : else
661 : 0 : *buf = NULL; /* fallback in gstpad.c will allocate generic buffer */
662 : :
663 : 0 : gst_object_unref (bsink);
664 : :
665 : 0 : return result;
666 : : }
667 : :
668 : : static void
669 : 161 : gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
670 : : {
671 : : GstPadTemplate *pad_template;
672 : : GstBaseSinkPrivate *priv;
673 : :
674 : 161 : basesink->priv = priv = GST_BASE_SINK_GET_PRIVATE (basesink);
675 : :
676 : 161 : pad_template =
677 : 161 : gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
678 [ - + ]: 322 : g_return_if_fail (pad_template != NULL);
679 : :
680 : 161 : basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
681 : :
682 : 161 : gst_pad_set_getcaps_function (basesink->sinkpad, gst_base_sink_pad_getcaps);
683 : 161 : gst_pad_set_setcaps_function (basesink->sinkpad, gst_base_sink_pad_setcaps);
684 : 161 : gst_pad_set_fixatecaps_function (basesink->sinkpad, gst_base_sink_pad_fixate);
685 : 161 : gst_pad_set_bufferalloc_function (basesink->sinkpad,
686 : : gst_base_sink_pad_buffer_alloc);
687 : 161 : gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate);
688 : 161 : gst_pad_set_activatepush_function (basesink->sinkpad,
689 : : gst_base_sink_pad_activate_push);
690 : 161 : gst_pad_set_activatepull_function (basesink->sinkpad,
691 : : gst_base_sink_pad_activate_pull);
692 : 161 : gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event);
693 : 161 : gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain);
694 : 161 : gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list);
695 : 161 : gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
696 : :
697 : 161 : basesink->pad_mode = GST_ACTIVATE_NONE;
698 : 161 : basesink->preroll_queue = g_queue_new ();
699 : 161 : basesink->abidata.ABI.clip_segment = gst_segment_new ();
700 : 161 : priv->have_latency = FALSE;
701 : :
702 : 161 : basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
703 : 161 : basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
704 : :
705 : 161 : basesink->sync = DEFAULT_SYNC;
706 : 161 : basesink->abidata.ABI.max_lateness = DEFAULT_MAX_LATENESS;
707 : 161 : g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS);
708 : 161 : priv->async_enabled = DEFAULT_ASYNC;
709 : 161 : priv->ts_offset = DEFAULT_TS_OFFSET;
710 : 161 : priv->render_delay = DEFAULT_RENDER_DELAY;
711 : 161 : priv->blocksize = DEFAULT_BLOCKSIZE;
712 : 161 : priv->cached_clock_id = NULL;
713 : 161 : g_atomic_int_set (&priv->enable_last_buffer, DEFAULT_ENABLE_LAST_BUFFER);
714 : 161 : priv->throttle_time = DEFAULT_THROTTLE_TIME;
715 : :
716 : 161 : GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_IS_SINK);
717 : : }
718 : :
719 : : static void
720 : 151 : gst_base_sink_finalize (GObject * object)
721 : : {
722 : : GstBaseSink *basesink;
723 : :
724 : 151 : basesink = GST_BASE_SINK (object);
725 : :
726 : 151 : g_queue_free (basesink->preroll_queue);
727 : 151 : gst_segment_free (basesink->abidata.ABI.clip_segment);
728 : :
729 : 151 : G_OBJECT_CLASS (parent_class)->finalize (object);
730 : 151 : }
731 : :
732 : : /**
733 : : * gst_base_sink_set_sync:
734 : : * @sink: the sink
735 : : * @sync: the new sync value.
736 : : *
737 : : * Configures @sink to synchronize on the clock or not. When
738 : : * @sync is FALSE, incomming samples will be played as fast as
739 : : * possible. If @sync is TRUE, the timestamps of the incomming
740 : : * buffers will be used to schedule the exact render time of its
741 : : * contents.
742 : : *
743 : : * Since: 0.10.4
744 : : */
745 : : void
746 : 173 : gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
747 : : {
748 [ - + ][ + - ]: 346 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ - + ][ - + ]
749 : :
750 : 173 : GST_OBJECT_LOCK (sink);
751 : 173 : sink->sync = sync;
752 : 173 : GST_OBJECT_UNLOCK (sink);
753 : : }
754 : :
755 : : /**
756 : : * gst_base_sink_get_sync:
757 : : * @sink: the sink
758 : : *
759 : : * Checks if @sink is currently configured to synchronize against the
760 : : * clock.
761 : : *
762 : : * Returns: TRUE if the sink is configured to synchronize against the clock.
763 : : *
764 : : * Since: 0.10.4
765 : : */
766 : : gboolean
767 : 1 : gst_base_sink_get_sync (GstBaseSink * sink)
768 : : {
769 : : gboolean res;
770 : :
771 [ - + ][ + - ]: 1 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
[ - + ][ - + ]
772 : :
773 : 1 : GST_OBJECT_LOCK (sink);
774 : 1 : res = sink->sync;
775 : 1 : GST_OBJECT_UNLOCK (sink);
776 : :
777 : 1 : return res;
778 : : }
779 : :
780 : : /**
781 : : * gst_base_sink_set_max_lateness:
782 : : * @sink: the sink
783 : : * @max_lateness: the new max lateness value.
784 : : *
785 : : * Sets the new max lateness value to @max_lateness. This value is
786 : : * used to decide if a buffer should be dropped or not based on the
787 : : * buffer timestamp and the current clock time. A value of -1 means
788 : : * an unlimited time.
789 : : *
790 : : * Since: 0.10.4
791 : : */
792 : : void
793 : 0 : gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
794 : : {
795 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
796 : :
797 : 0 : GST_OBJECT_LOCK (sink);
798 : 0 : sink->abidata.ABI.max_lateness = max_lateness;
799 : 0 : GST_OBJECT_UNLOCK (sink);
800 : : }
801 : :
802 : : /**
803 : : * gst_base_sink_get_max_lateness:
804 : : * @sink: the sink
805 : : *
806 : : * Gets the max lateness value. See gst_base_sink_set_max_lateness for
807 : : * more details.
808 : : *
809 : : * Returns: The maximum time in nanoseconds that a buffer can be late
810 : : * before it is dropped and not rendered. A value of -1 means an
811 : : * unlimited time.
812 : : *
813 : : * Since: 0.10.4
814 : : */
815 : : gint64
816 : 0 : gst_base_sink_get_max_lateness (GstBaseSink * sink)
817 : : {
818 : : gint64 res;
819 : :
820 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), -1);
[ # # ][ # # ]
821 : :
822 : 0 : GST_OBJECT_LOCK (sink);
823 : 0 : res = sink->abidata.ABI.max_lateness;
824 : 0 : GST_OBJECT_UNLOCK (sink);
825 : :
826 : 0 : return res;
827 : : }
828 : :
829 : : /**
830 : : * gst_base_sink_set_qos_enabled:
831 : : * @sink: the sink
832 : : * @enabled: the new qos value.
833 : : *
834 : : * Configures @sink to send Quality-of-Service events upstream.
835 : : *
836 : : * Since: 0.10.5
837 : : */
838 : : void
839 : 0 : gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
840 : : {
841 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
842 : :
843 : 0 : g_atomic_int_set (&sink->priv->qos_enabled, enabled);
844 : : }
845 : :
846 : : /**
847 : : * gst_base_sink_is_qos_enabled:
848 : : * @sink: the sink
849 : : *
850 : : * Checks if @sink is currently configured to send Quality-of-Service events
851 : : * upstream.
852 : : *
853 : : * Returns: TRUE if the sink is configured to perform Quality-of-Service.
854 : : *
855 : : * Since: 0.10.5
856 : : */
857 : : gboolean
858 : 0 : gst_base_sink_is_qos_enabled (GstBaseSink * sink)
859 : : {
860 : : gboolean res;
861 : :
862 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
[ # # ][ # # ]
863 : :
864 : 0 : res = g_atomic_int_get (&sink->priv->qos_enabled);
865 : :
866 : 0 : return res;
867 : : }
868 : :
869 : : /**
870 : : * gst_base_sink_set_async_enabled:
871 : : * @sink: the sink
872 : : * @enabled: the new async value.
873 : : *
874 : : * Configures @sink to perform all state changes asynchronusly. When async is
875 : : * disabled, the sink will immediatly go to PAUSED instead of waiting for a
876 : : * preroll buffer. This feature is usefull if the sink does not synchronize
877 : : * against the clock or when it is dealing with sparse streams.
878 : : *
879 : : * Since: 0.10.15
880 : : */
881 : : void
882 : 2 : gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
883 : : {
884 [ - + ][ + - ]: 4 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ - + ][ - + ]
885 : :
886 : 2 : GST_PAD_PREROLL_LOCK (sink->sinkpad);
887 : 2 : g_atomic_int_set (&sink->priv->async_enabled, enabled);
888 [ - + ]: 2 : GST_LOG_OBJECT (sink, "set async enabled to %d", enabled);
889 : 2 : GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
890 : : }
891 : :
892 : : /**
893 : : * gst_base_sink_is_async_enabled:
894 : : * @sink: the sink
895 : : *
896 : : * Checks if @sink is currently configured to perform asynchronous state
897 : : * changes to PAUSED.
898 : : *
899 : : * Returns: TRUE if the sink is configured to perform asynchronous state
900 : : * changes.
901 : : *
902 : : * Since: 0.10.15
903 : : */
904 : : gboolean
905 : 0 : gst_base_sink_is_async_enabled (GstBaseSink * sink)
906 : : {
907 : : gboolean res;
908 : :
909 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
[ # # ][ # # ]
910 : :
911 : 0 : res = g_atomic_int_get (&sink->priv->async_enabled);
912 : :
913 : 0 : return res;
914 : : }
915 : :
916 : : /**
917 : : * gst_base_sink_set_ts_offset:
918 : : * @sink: the sink
919 : : * @offset: the new offset
920 : : *
921 : : * Adjust the synchronisation of @sink with @offset. A negative value will
922 : : * render buffers earlier than their timestamp. A positive value will delay
923 : : * rendering. This function can be used to fix playback of badly timestamped
924 : : * buffers.
925 : : *
926 : : * Since: 0.10.15
927 : : */
928 : : void
929 : 0 : gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
930 : : {
931 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
932 : :
933 : 0 : GST_OBJECT_LOCK (sink);
934 : 0 : sink->priv->ts_offset = offset;
935 [ # # ]: 0 : GST_LOG_OBJECT (sink, "set time offset to %" G_GINT64_FORMAT, offset);
936 : 0 : GST_OBJECT_UNLOCK (sink);
937 : : }
938 : :
939 : : /**
940 : : * gst_base_sink_get_ts_offset:
941 : : * @sink: the sink
942 : : *
943 : : * Get the synchronisation offset of @sink.
944 : : *
945 : : * Returns: The synchronisation offset.
946 : : *
947 : : * Since: 0.10.15
948 : : */
949 : : GstClockTimeDiff
950 : 0 : gst_base_sink_get_ts_offset (GstBaseSink * sink)
951 : : {
952 : : GstClockTimeDiff res;
953 : :
954 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
[ # # ][ # # ]
955 : :
956 : 0 : GST_OBJECT_LOCK (sink);
957 : 0 : res = sink->priv->ts_offset;
958 : 0 : GST_OBJECT_UNLOCK (sink);
959 : :
960 : 0 : return res;
961 : : }
962 : :
963 : : /**
964 : : * gst_base_sink_get_last_buffer:
965 : : * @sink: the sink
966 : : *
967 : : * Get the last buffer that arrived in the sink and was used for preroll or for
968 : : * rendering. This property can be used to generate thumbnails.
969 : : *
970 : : * The #GstCaps on the buffer can be used to determine the type of the buffer.
971 : : *
972 : : * Free-function: gst_buffer_unref
973 : : *
974 : : * Returns: (transfer full): a #GstBuffer. gst_buffer_unref() after usage.
975 : : * This function returns NULL when no buffer has arrived in the sink yet
976 : : * or when the sink is not in PAUSED or PLAYING.
977 : : *
978 : : * Since: 0.10.15
979 : : */
980 : : GstBuffer *
981 : 3 : gst_base_sink_get_last_buffer (GstBaseSink * sink)
982 : : {
983 : : GstBuffer *res;
984 : :
985 [ - + ][ + - ]: 3 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);
[ - + ][ - + ]
986 : :
987 : 3 : GST_OBJECT_LOCK (sink);
988 [ + + ]: 3 : if ((res = sink->priv->last_buffer))
989 : 1 : gst_buffer_ref (res);
990 : 3 : GST_OBJECT_UNLOCK (sink);
991 : :
992 : 3 : return res;
993 : : }
994 : :
995 : : /* with OBJECT_LOCK */
996 : : static void
997 : 686842 : gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
998 : : {
999 : : GstBuffer *old;
1000 : :
1001 : 686842 : old = sink->priv->last_buffer;
1002 [ + + ]: 686842 : if (G_LIKELY (old != buffer)) {
1003 [ - + ]: 686239 : GST_DEBUG_OBJECT (sink, "setting last buffer to %p", buffer);
1004 [ + + ]: 686238 : if (G_LIKELY (buffer))
1005 : 686082 : gst_buffer_ref (buffer);
1006 : 686240 : sink->priv->last_buffer = buffer;
1007 : : } else {
1008 : 603 : old = NULL;
1009 : : }
1010 : : /* avoid unreffing with the lock because cleanup code might want to take the
1011 : : * lock too */
1012 [ + + ]: 686843 : if (G_LIKELY (old)) {
1013 : 686084 : GST_OBJECT_UNLOCK (sink);
1014 : 686083 : gst_buffer_unref (old);
1015 : 686084 : GST_OBJECT_LOCK (sink);
1016 : : }
1017 : 686843 : }
1018 : :
1019 : : static void
1020 : 686846 : gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
1021 : : {
1022 [ + + ]: 686846 : if (!g_atomic_int_get (&sink->priv->enable_last_buffer))
1023 : 686847 : return;
1024 : :
1025 : 686841 : GST_OBJECT_LOCK (sink);
1026 : 686840 : gst_base_sink_set_last_buffer_unlocked (sink, buffer);
1027 : 686838 : GST_OBJECT_UNLOCK (sink);
1028 : : }
1029 : :
1030 : : /**
1031 : : * gst_base_sink_set_last_buffer_enabled:
1032 : : * @sink: the sink
1033 : : * @enabled: the new enable-last-buffer value.
1034 : : *
1035 : : * Configures @sink to store the last received buffer in the last-buffer
1036 : : * property.
1037 : : *
1038 : : * Since: 0.10.30
1039 : : */
1040 : : void
1041 : 2 : gst_base_sink_set_last_buffer_enabled (GstBaseSink * sink, gboolean enabled)
1042 : : {
1043 [ - + ][ + - ]: 4 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ - + ][ - + ]
1044 : :
1045 : : /* Only take lock if we change the value */
1046 [ + - ]: 2 : if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_buffer,
1047 [ + - ]: 2 : !enabled, enabled) && !enabled) {
1048 : 2 : GST_OBJECT_LOCK (sink);
1049 : 2 : gst_base_sink_set_last_buffer_unlocked (sink, NULL);
1050 : 2 : GST_OBJECT_UNLOCK (sink);
1051 : : }
1052 : : }
1053 : :
1054 : : /**
1055 : : * gst_base_sink_is_last_buffer_enabled:
1056 : : * @sink: the sink
1057 : : *
1058 : : * Checks if @sink is currently configured to store the last received buffer in
1059 : : * the last-buffer property.
1060 : : *
1061 : : * Returns: TRUE if the sink is configured to store the last received buffer.
1062 : : *
1063 : : * Since: 0.10.30
1064 : : */
1065 : : gboolean
1066 : 2 : gst_base_sink_is_last_buffer_enabled (GstBaseSink * sink)
1067 : : {
1068 [ - + ][ + - ]: 2 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
[ - + ][ - + ]
1069 : :
1070 : 2 : return g_atomic_int_get (&sink->priv->enable_last_buffer);
1071 : : }
1072 : :
1073 : : /**
1074 : : * gst_base_sink_get_latency:
1075 : : * @sink: the sink
1076 : : *
1077 : : * Get the currently configured latency.
1078 : : *
1079 : : * Returns: The configured latency.
1080 : : *
1081 : : * Since: 0.10.12
1082 : : */
1083 : : GstClockTime
1084 : 0 : gst_base_sink_get_latency (GstBaseSink * sink)
1085 : : {
1086 : : GstClockTime res;
1087 : :
1088 : 0 : GST_OBJECT_LOCK (sink);
1089 : 0 : res = sink->priv->latency;
1090 : 0 : GST_OBJECT_UNLOCK (sink);
1091 : :
1092 : 0 : return res;
1093 : : }
1094 : :
1095 : : /**
1096 : : * gst_base_sink_query_latency:
1097 : : * @sink: the sink
1098 : : * @live: (out) (allow-none): if the sink is live
1099 : : * @upstream_live: (out) (allow-none): if an upstream element is live
1100 : : * @min_latency: (out) (allow-none): the min latency of the upstream elements
1101 : : * @max_latency: (out) (allow-none): the max latency of the upstream elements
1102 : : *
1103 : : * Query the sink for the latency parameters. The latency will be queried from
1104 : : * the upstream elements. @live will be TRUE if @sink is configured to
1105 : : * synchronize against the clock. @upstream_live will be TRUE if an upstream
1106 : : * element is live.
1107 : : *
1108 : : * If both @live and @upstream_live are TRUE, the sink will want to compensate
1109 : : * for the latency introduced by the upstream elements by setting the
1110 : : * @min_latency to a strictly possitive value.
1111 : : *
1112 : : * This function is mostly used by subclasses.
1113 : : *
1114 : : * Returns: TRUE if the query succeeded.
1115 : : *
1116 : : * Since: 0.10.12
1117 : : */
1118 : : gboolean
1119 : 118 : gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
1120 : : gboolean * upstream_live, GstClockTime * min_latency,
1121 : : GstClockTime * max_latency)
1122 : : {
1123 : : gboolean l, us_live, res, have_latency;
1124 : : GstClockTime min, max, render_delay;
1125 : : GstQuery *query;
1126 : : GstClockTime us_min, us_max;
1127 : :
1128 : : /* we are live when we sync to the clock */
1129 : 118 : GST_OBJECT_LOCK (sink);
1130 : 118 : l = sink->sync;
1131 : 118 : have_latency = sink->priv->have_latency;
1132 : 118 : render_delay = sink->priv->render_delay;
1133 : 118 : GST_OBJECT_UNLOCK (sink);
1134 : :
1135 : : /* assume no latency */
1136 : 118 : min = 0;
1137 : 118 : max = -1;
1138 : 118 : us_live = FALSE;
1139 : :
1140 [ + + ]: 118 : if (have_latency) {
1141 [ - + ]: 110 : GST_DEBUG_OBJECT (sink, "we are ready for LATENCY query");
1142 : : /* we are ready for a latency query this is when we preroll or when we are
1143 : : * not async. */
1144 : 110 : query = gst_query_new_latency ();
1145 : :
1146 : : /* ask the peer for the latency */
1147 [ + + ]: 110 : if ((res = gst_pad_peer_query (sink->sinkpad, query))) {
1148 : : /* get upstream min and max latency */
1149 : 102 : gst_query_parse_latency (query, &us_live, &us_min, &us_max);
1150 : :
1151 [ + + ]: 102 : if (us_live) {
1152 : : /* upstream live, use its latency, subclasses should use these
1153 : : * values to create the complete latency. */
1154 : 7 : min = us_min;
1155 : 7 : max = us_max;
1156 : : }
1157 [ + + ]: 102 : if (l) {
1158 : : /* we need to add the render delay if we are live */
1159 [ + - ]: 9 : if (min != -1)
1160 : 9 : min += render_delay;
1161 [ - + ]: 9 : if (max != -1)
1162 : 0 : max += render_delay;
1163 : : }
1164 : : }
1165 : 110 : gst_query_unref (query);
1166 : : } else {
1167 [ - + ]: 8 : GST_DEBUG_OBJECT (sink, "we are not yet ready for LATENCY query");
1168 : 8 : res = FALSE;
1169 : : }
1170 : :
1171 : : /* not live, we tried to do the query, if it failed we return TRUE anyway */
1172 [ + + ]: 118 : if (!res) {
1173 [ + + ]: 16 : if (!l) {
1174 : 8 : res = TRUE;
1175 [ - + ]: 8 : GST_DEBUG_OBJECT (sink, "latency query failed but we are not live");
1176 : : } else {
1177 [ - + ]: 8 : GST_DEBUG_OBJECT (sink, "latency query failed and we are live");
1178 : : }
1179 : : }
1180 : :
1181 [ + + ]: 118 : if (res) {
1182 [ - + ][ # # ]: 110 : GST_DEBUG_OBJECT (sink, "latency query: live: %d, have_latency %d,"
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1183 : : " upstream: %d, min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, l,
1184 : : have_latency, us_live, GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1185 : :
1186 [ + - ]: 110 : if (live)
1187 : 110 : *live = l;
1188 [ + - ]: 110 : if (upstream_live)
1189 : 110 : *upstream_live = us_live;
1190 [ + - ]: 110 : if (min_latency)
1191 : 110 : *min_latency = min;
1192 [ + - ]: 110 : if (max_latency)
1193 : 110 : *max_latency = max;
1194 : : }
1195 : 118 : return res;
1196 : : }
1197 : :
1198 : : /**
1199 : : * gst_base_sink_set_render_delay:
1200 : : * @sink: a #GstBaseSink
1201 : : * @delay: the new delay
1202 : : *
1203 : : * Set the render delay in @sink to @delay. The render delay is the time
1204 : : * between actual rendering of a buffer and its synchronisation time. Some
1205 : : * devices might delay media rendering which can be compensated for with this
1206 : : * function.
1207 : : *
1208 : : * After calling this function, this sink will report additional latency and
1209 : : * other sinks will adjust their latency to delay the rendering of their media.
1210 : : *
1211 : : * This function is usually called by subclasses.
1212 : : *
1213 : : * Since: 0.10.21
1214 : : */
1215 : : void
1216 : 0 : gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
1217 : : {
1218 : : GstClockTime old_render_delay;
1219 : :
1220 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
1221 : :
1222 : 0 : GST_OBJECT_LOCK (sink);
1223 : 0 : old_render_delay = sink->priv->render_delay;
1224 : 0 : sink->priv->render_delay = delay;
1225 [ # # ][ # # ]: 0 : GST_LOG_OBJECT (sink, "set render delay to %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1226 : : GST_TIME_ARGS (delay));
1227 : 0 : GST_OBJECT_UNLOCK (sink);
1228 : :
1229 [ # # ]: 0 : if (delay != old_render_delay) {
1230 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "posting latency changed");
1231 : 0 : gst_element_post_message (GST_ELEMENT_CAST (sink),
1232 : : gst_message_new_latency (GST_OBJECT_CAST (sink)));
1233 : : }
1234 : : }
1235 : :
1236 : : /**
1237 : : * gst_base_sink_get_render_delay:
1238 : : * @sink: a #GstBaseSink
1239 : : *
1240 : : * Get the render delay of @sink. see gst_base_sink_set_render_delay() for more
1241 : : * information about the render delay.
1242 : : *
1243 : : * Returns: the render delay of @sink.
1244 : : *
1245 : : * Since: 0.10.21
1246 : : */
1247 : : GstClockTime
1248 : 0 : gst_base_sink_get_render_delay (GstBaseSink * sink)
1249 : : {
1250 : : GstClockTimeDiff res;
1251 : :
1252 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
[ # # ][ # # ]
1253 : :
1254 : 0 : GST_OBJECT_LOCK (sink);
1255 : 0 : res = sink->priv->render_delay;
1256 : 0 : GST_OBJECT_UNLOCK (sink);
1257 : :
1258 : 0 : return res;
1259 : : }
1260 : :
1261 : : /**
1262 : : * gst_base_sink_set_blocksize:
1263 : : * @sink: a #GstBaseSink
1264 : : * @blocksize: the blocksize in bytes
1265 : : *
1266 : : * Set the number of bytes that the sink will pull when it is operating in pull
1267 : : * mode.
1268 : : *
1269 : : * Since: 0.10.22
1270 : : */
1271 : : /* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
1272 : : void
1273 : 0 : gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
1274 : : {
1275 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
1276 : :
1277 : 0 : GST_OBJECT_LOCK (sink);
1278 : 0 : sink->priv->blocksize = blocksize;
1279 [ # # ]: 0 : GST_LOG_OBJECT (sink, "set blocksize to %u", blocksize);
1280 : 0 : GST_OBJECT_UNLOCK (sink);
1281 : : }
1282 : :
1283 : : /**
1284 : : * gst_base_sink_get_blocksize:
1285 : : * @sink: a #GstBaseSink
1286 : : *
1287 : : * Get the number of bytes that the sink will pull when it is operating in pull
1288 : : * mode.
1289 : : *
1290 : : * Returns: the number of bytes @sink will pull in pull mode.
1291 : : *
1292 : : * Since: 0.10.22
1293 : : */
1294 : : /* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
1295 : : guint
1296 : 0 : gst_base_sink_get_blocksize (GstBaseSink * sink)
1297 : : {
1298 : : guint res;
1299 : :
1300 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
[ # # ][ # # ]
1301 : :
1302 : 0 : GST_OBJECT_LOCK (sink);
1303 : 0 : res = sink->priv->blocksize;
1304 : 0 : GST_OBJECT_UNLOCK (sink);
1305 : :
1306 : 0 : return res;
1307 : : }
1308 : :
1309 : : /**
1310 : : * gst_base_sink_set_throttle_time:
1311 : : * @sink: a #GstBaseSink
1312 : : * @throttle: the throttle time in nanoseconds
1313 : : *
1314 : : * Set the time that will be inserted between rendered buffers. This
1315 : : * can be used to control the maximum buffers per second that the sink
1316 : : * will render.
1317 : : *
1318 : : * Since: 0.10.33
1319 : : */
1320 : : void
1321 : 0 : gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
1322 : : {
1323 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_BASE_SINK (sink));
[ # # ][ # # ]
1324 : :
1325 : 0 : GST_OBJECT_LOCK (sink);
1326 : 0 : sink->priv->throttle_time = throttle;
1327 [ # # ]: 0 : GST_LOG_OBJECT (sink, "set throttle_time to %" G_GUINT64_FORMAT, throttle);
1328 : 0 : GST_OBJECT_UNLOCK (sink);
1329 : : }
1330 : :
1331 : : /**
1332 : : * gst_base_sink_get_throttle_time:
1333 : : * @sink: a #GstBaseSink
1334 : : *
1335 : : * Get the time that will be inserted between frames to control the
1336 : : * maximum buffers per second.
1337 : : *
1338 : : * Returns: the number of nanoseconds @sink will put between frames.
1339 : : *
1340 : : * Since: 0.10.33
1341 : : */
1342 : : guint64
1343 : 0 : gst_base_sink_get_throttle_time (GstBaseSink * sink)
1344 : : {
1345 : : guint64 res;
1346 : :
1347 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
[ # # ][ # # ]
1348 : :
1349 : 0 : GST_OBJECT_LOCK (sink);
1350 : 0 : res = sink->priv->throttle_time;
1351 : 0 : GST_OBJECT_UNLOCK (sink);
1352 : :
1353 : 0 : return res;
1354 : : }
1355 : :
1356 : : static void
1357 : 179 : gst_base_sink_set_property (GObject * object, guint prop_id,
1358 : : const GValue * value, GParamSpec * pspec)
1359 : : {
1360 : 179 : GstBaseSink *sink = GST_BASE_SINK (object);
1361 : :
1362 [ + + - - : 179 : switch (prop_id) {
+ - - - +
- - ]
1363 : : case PROP_PREROLL_QUEUE_LEN:
1364 : : /* preroll lock necessary to serialize with finish_preroll */
1365 : 164 : GST_PAD_PREROLL_LOCK (sink->sinkpad);
1366 : 164 : g_atomic_int_set (&sink->preroll_queue_max_len, g_value_get_uint (value));
1367 : 164 : GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
1368 : 164 : break;
1369 : : case PROP_SYNC:
1370 : 12 : gst_base_sink_set_sync (sink, g_value_get_boolean (value));
1371 : 12 : break;
1372 : : case PROP_MAX_LATENESS:
1373 : 0 : gst_base_sink_set_max_lateness (sink, g_value_get_int64 (value));
1374 : 0 : break;
1375 : : case PROP_QOS:
1376 : 0 : gst_base_sink_set_qos_enabled (sink, g_value_get_boolean (value));
1377 : 0 : break;
1378 : : case PROP_ASYNC:
1379 : 2 : gst_base_sink_set_async_enabled (sink, g_value_get_boolean (value));
1380 : 2 : break;
1381 : : case PROP_TS_OFFSET:
1382 : 0 : gst_base_sink_set_ts_offset (sink, g_value_get_int64 (value));
1383 : 0 : break;
1384 : : case PROP_BLOCKSIZE:
1385 : 0 : gst_base_sink_set_blocksize (sink, g_value_get_uint (value));
1386 : 0 : break;
1387 : : case PROP_RENDER_DELAY:
1388 : 0 : gst_base_sink_set_render_delay (sink, g_value_get_uint64 (value));
1389 : 0 : break;
1390 : : case PROP_ENABLE_LAST_BUFFER:
1391 : 1 : gst_base_sink_set_last_buffer_enabled (sink, g_value_get_boolean (value));
1392 : 1 : break;
1393 : : case PROP_THROTTLE_TIME:
1394 : 0 : gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value));
1395 : 0 : break;
1396 : : default:
1397 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1398 : 0 : break;
1399 : : }
1400 : 179 : }
1401 : :
1402 : : static void
1403 : 3 : gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
1404 : : GParamSpec * pspec)
1405 : : {
1406 : 3 : GstBaseSink *sink = GST_BASE_SINK (object);
1407 : :
1408 [ - - - - : 3 : switch (prop_id) {
- - + - -
- - - ]
1409 : : case PROP_PREROLL_QUEUE_LEN:
1410 : 0 : g_value_set_uint (value, g_atomic_int_get (&sink->preroll_queue_max_len));
1411 : 0 : break;
1412 : : case PROP_SYNC:
1413 : 0 : g_value_set_boolean (value, gst_base_sink_get_sync (sink));
1414 : 0 : break;
1415 : : case PROP_MAX_LATENESS:
1416 : 0 : g_value_set_int64 (value, gst_base_sink_get_max_lateness (sink));
1417 : 0 : break;
1418 : : case PROP_QOS:
1419 : 0 : g_value_set_boolean (value, gst_base_sink_is_qos_enabled (sink));
1420 : 0 : break;
1421 : : case PROP_ASYNC:
1422 : 0 : g_value_set_boolean (value, gst_base_sink_is_async_enabled (sink));
1423 : 0 : break;
1424 : : case PROP_TS_OFFSET:
1425 : 0 : g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink));
1426 : 0 : break;
1427 : : case PROP_LAST_BUFFER:
1428 : 3 : gst_value_take_buffer (value, gst_base_sink_get_last_buffer (sink));
1429 : 3 : break;
1430 : : case PROP_ENABLE_LAST_BUFFER:
1431 : 0 : g_value_set_boolean (value, gst_base_sink_is_last_buffer_enabled (sink));
1432 : 0 : break;
1433 : : case PROP_BLOCKSIZE:
1434 : 0 : g_value_set_uint (value, gst_base_sink_get_blocksize (sink));
1435 : 0 : break;
1436 : : case PROP_RENDER_DELAY:
1437 : 0 : g_value_set_uint64 (value, gst_base_sink_get_render_delay (sink));
1438 : 0 : break;
1439 : : case PROP_THROTTLE_TIME:
1440 : 0 : g_value_set_uint64 (value, gst_base_sink_get_throttle_time (sink));
1441 : 0 : break;
1442 : : default:
1443 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1444 : 0 : break;
1445 : : }
1446 : 3 : }
1447 : :
1448 : :
1449 : : static GstCaps *
1450 : 260 : gst_base_sink_get_caps (GstBaseSink * sink)
1451 : : {
1452 : 260 : return NULL;
1453 : : }
1454 : :
1455 : : static gboolean
1456 : 4 : gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
1457 : : {
1458 : 4 : return TRUE;
1459 : : }
1460 : :
1461 : : static GstFlowReturn
1462 : 0 : gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size,
1463 : : GstCaps * caps, GstBuffer ** buf)
1464 : : {
1465 : 0 : *buf = NULL;
1466 : 0 : return GST_FLOW_OK;
1467 : : }
1468 : :
1469 : : /* with PREROLL_LOCK, STREAM_LOCK */
1470 : : static void
1471 : 429 : gst_base_sink_preroll_queue_flush (GstBaseSink * basesink, GstPad * pad)
1472 : : {
1473 : : GstMiniObject *obj;
1474 : :
1475 [ - + ]: 429 : GST_DEBUG_OBJECT (basesink, "flushing queue %p", basesink);
1476 [ + + ]: 441 : while ((obj = g_queue_pop_head (basesink->preroll_queue))) {
1477 [ - + ]: 12 : GST_DEBUG_OBJECT (basesink, "popped %p", obj);
1478 : 12 : gst_mini_object_unref (obj);
1479 : : }
1480 : : /* we can't have EOS anymore now */
1481 : 429 : basesink->eos = FALSE;
1482 : 429 : basesink->priv->received_eos = FALSE;
1483 : 429 : basesink->have_preroll = FALSE;
1484 : 429 : basesink->priv->step_unlock = FALSE;
1485 : 429 : basesink->eos_queued = FALSE;
1486 : 429 : basesink->preroll_queued = 0;
1487 : 429 : basesink->buffers_queued = 0;
1488 : 429 : basesink->events_queued = 0;
1489 : : /* can't report latency anymore until we preroll again */
1490 [ + + ]: 429 : if (basesink->priv->async_enabled) {
1491 : 425 : GST_OBJECT_LOCK (basesink);
1492 : 425 : basesink->priv->have_latency = FALSE;
1493 : 425 : GST_OBJECT_UNLOCK (basesink);
1494 : : }
1495 : : /* and signal any waiters now */
1496 : 429 : GST_PAD_PREROLL_SIGNAL (pad);
1497 : 429 : }
1498 : :
1499 : : /* with STREAM_LOCK, configures given segment with the event information. */
1500 : : static void
1501 : 308 : gst_base_sink_configure_segment (GstBaseSink * basesink, GstPad * pad,
1502 : : GstEvent * event, GstSegment * segment)
1503 : : {
1504 : : gboolean update;
1505 : : gdouble rate, arate;
1506 : : GstFormat format;
1507 : : gint64 start;
1508 : : gint64 stop;
1509 : : gint64 time;
1510 : :
1511 : : /* the newsegment event is needed to bring the buffer timestamps to the
1512 : : * stream time and to drop samples outside of the playback segment. */
1513 : 308 : gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
1514 : : &start, &stop, &time);
1515 : :
1516 : : /* The segment is protected with both the STREAM_LOCK and the OBJECT_LOCK.
1517 : : * We protect with the OBJECT_LOCK so that we can use the values to
1518 : : * safely answer a POSITION query. */
1519 : 308 : GST_OBJECT_LOCK (basesink);
1520 : 308 : gst_segment_set_newsegment_full (segment, update, rate, arate, format, start,
1521 : : stop, time);
1522 : :
1523 [ + + ]: 308 : if (format == GST_FORMAT_TIME) {
1524 [ - + ][ # # ]: 17 : GST_DEBUG_OBJECT (basesink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1525 : : "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
1526 : : "format GST_FORMAT_TIME, "
1527 : : "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
1528 : : ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
1529 : : update, rate, arate, GST_TIME_ARGS (segment->start),
1530 : : GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
1531 : : GST_TIME_ARGS (segment->accum));
1532 : : } else {
1533 [ - + ]: 291 : GST_DEBUG_OBJECT (basesink,
1534 : : "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
1535 : : "format %d, "
1536 : : "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
1537 : : G_GINT64_FORMAT ", accum %" G_GINT64_FORMAT, update, rate, arate,
1538 : : segment->format, segment->start, segment->stop, segment->time,
1539 : : segment->accum);
1540 : : }
1541 : 308 : GST_OBJECT_UNLOCK (basesink);
1542 : 308 : }
1543 : :
1544 : : /* with PREROLL_LOCK, STREAM_LOCK */
1545 : : static gboolean
1546 : 187 : gst_base_sink_commit_state (GstBaseSink * basesink)
1547 : : {
1548 : : /* commit state and proceed to next pending state */
1549 : : GstState current, next, pending, post_pending;
1550 : 187 : gboolean post_paused = FALSE;
1551 : 187 : gboolean post_async_done = FALSE;
1552 : 187 : gboolean post_playing = FALSE;
1553 : :
1554 : : /* we are certainly not playing async anymore now */
1555 : 187 : basesink->playing_async = FALSE;
1556 : :
1557 : 187 : GST_OBJECT_LOCK (basesink);
1558 : 187 : current = GST_STATE (basesink);
1559 : 187 : next = GST_STATE_NEXT (basesink);
1560 : 187 : pending = GST_STATE_PENDING (basesink);
1561 : 187 : post_pending = pending;
1562 : :
1563 [ + + + + : 187 : switch (pending) {
- ]
1564 : : case GST_STATE_PLAYING:
1565 : : {
1566 : : GstBaseSinkClass *bclass;
1567 : : GstStateChangeReturn ret;
1568 : :
1569 : 2 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
1570 : :
1571 [ - + ]: 2 : GST_DEBUG_OBJECT (basesink, "commiting state to PLAYING");
1572 : :
1573 : 2 : basesink->need_preroll = FALSE;
1574 : 2 : post_async_done = TRUE;
1575 : 2 : basesink->priv->commited = TRUE;
1576 : 2 : post_playing = TRUE;
1577 : : /* post PAUSED too when we were READY */
1578 [ + - ]: 2 : if (current == GST_STATE_READY) {
1579 : 2 : post_paused = TRUE;
1580 : : }
1581 : :
1582 : : /* make sure we notify the subclass of async playing */
1583 [ - + ]: 2 : if (bclass->async_play) {
1584 [ # # ]: 0 : GST_WARNING_OBJECT (basesink, "deprecated async_play");
1585 : 0 : ret = bclass->async_play (basesink);
1586 [ # # ]: 0 : if (ret == GST_STATE_CHANGE_FAILURE)
1587 : 0 : goto async_failed;
1588 : : }
1589 : 2 : break;
1590 : : }
1591 : : case GST_STATE_PAUSED:
1592 [ - + ]: 176 : GST_DEBUG_OBJECT (basesink, "commiting state to PAUSED");
1593 : 176 : post_paused = TRUE;
1594 : 176 : post_async_done = TRUE;
1595 : 176 : basesink->priv->commited = TRUE;
1596 : 176 : post_pending = GST_STATE_VOID_PENDING;
1597 : 176 : break;
1598 : : case GST_STATE_READY:
1599 : : case GST_STATE_NULL:
1600 : 4 : goto stopping;
1601 : : case GST_STATE_VOID_PENDING:
1602 : 5 : goto nothing_pending;
1603 : : default:
1604 : 0 : break;
1605 : : }
1606 : :
1607 : : /* we can report latency queries now */
1608 : 178 : basesink->priv->have_latency = TRUE;
1609 : :
1610 : 178 : GST_STATE (basesink) = pending;
1611 : 178 : GST_STATE_NEXT (basesink) = GST_STATE_VOID_PENDING;
1612 : 178 : GST_STATE_PENDING (basesink) = GST_STATE_VOID_PENDING;
1613 : 178 : GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_SUCCESS;
1614 : 178 : GST_OBJECT_UNLOCK (basesink);
1615 : :
1616 [ + - ]: 178 : if (post_paused) {
1617 [ - + ]: 178 : GST_DEBUG_OBJECT (basesink, "posting PAUSED state change message");
1618 : 178 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
1619 : : gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
1620 : : current, next, post_pending));
1621 : : }
1622 [ + - ]: 178 : if (post_async_done) {
1623 [ - + ]: 178 : GST_DEBUG_OBJECT (basesink, "posting async-done message");
1624 : 178 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
1625 : : gst_message_new_async_done (GST_OBJECT_CAST (basesink)));
1626 : : }
1627 [ + + ]: 178 : if (post_playing) {
1628 [ - + ]: 2 : GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
1629 : 2 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
1630 : : gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
1631 : : next, pending, GST_STATE_VOID_PENDING));
1632 : : }
1633 : :
1634 : 178 : GST_STATE_BROADCAST (basesink);
1635 : :
1636 : 178 : return TRUE;
1637 : :
1638 : : nothing_pending:
1639 : : {
1640 : : /* Depending on the state, set our vars. We get in this situation when the
1641 : : * state change function got a change to update the state vars before the
1642 : : * streaming thread did. This is fine but we need to make sure that we
1643 : : * update the need_preroll var since it was TRUE when we got here and might
1644 : : * become FALSE if we got to PLAYING. */
1645 [ - + ]: 5 : GST_DEBUG_OBJECT (basesink, "nothing to commit, now in %s",
1646 : : gst_element_state_get_name (current));
1647 [ + + - ]: 5 : switch (current) {
1648 : : case GST_STATE_PLAYING:
1649 : 2 : basesink->need_preroll = FALSE;
1650 : 2 : break;
1651 : : case GST_STATE_PAUSED:
1652 : 3 : basesink->need_preroll = TRUE;
1653 : 3 : break;
1654 : : default:
1655 : 0 : basesink->need_preroll = FALSE;
1656 : 0 : basesink->flushing = TRUE;
1657 : 0 : break;
1658 : : }
1659 : : /* we can report latency queries now */
1660 : 5 : basesink->priv->have_latency = TRUE;
1661 : 5 : GST_OBJECT_UNLOCK (basesink);
1662 : 5 : return TRUE;
1663 : : }
1664 : : stopping:
1665 : : {
1666 : : /* app is going to READY */
1667 [ - + ]: 4 : GST_DEBUG_OBJECT (basesink, "stopping");
1668 : 4 : basesink->need_preroll = FALSE;
1669 : 4 : basesink->flushing = TRUE;
1670 : 4 : GST_OBJECT_UNLOCK (basesink);
1671 : 4 : return FALSE;
1672 : : }
1673 : : async_failed:
1674 : : {
1675 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "async commit failed");
1676 : 0 : GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_FAILURE;
1677 : 0 : GST_OBJECT_UNLOCK (basesink);
1678 : 187 : return FALSE;
1679 : : }
1680 : : }
1681 : :
1682 : : static void
1683 : 0 : start_stepping (GstBaseSink * sink, GstSegment * segment,
1684 : : GstStepInfo * pending, GstStepInfo * current)
1685 : : {
1686 : : gint64 end;
1687 : : GstMessage *message;
1688 : :
1689 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "update pending step");
1690 : :
1691 : 0 : GST_OBJECT_LOCK (sink);
1692 : 0 : memcpy (current, pending, sizeof (GstStepInfo));
1693 : 0 : pending->valid = FALSE;
1694 : 0 : GST_OBJECT_UNLOCK (sink);
1695 : :
1696 : : /* post message first */
1697 : 0 : message =
1698 : 0 : gst_message_new_step_start (GST_OBJECT (sink), TRUE, current->format,
1699 : : current->amount, current->rate, current->flush, current->intermediate);
1700 : 0 : gst_message_set_seqnum (message, current->seqnum);
1701 : 0 : gst_element_post_message (GST_ELEMENT (sink), message);
1702 : :
1703 : : /* get the running time of where we paused and remember it */
1704 : 0 : current->start = gst_element_get_start_time (GST_ELEMENT_CAST (sink));
1705 : 0 : gst_segment_set_running_time (segment, GST_FORMAT_TIME, current->start);
1706 : :
1707 : : /* set the new rate for the remainder of the segment */
1708 : 0 : current->start_rate = segment->rate;
1709 : 0 : segment->rate *= current->rate;
1710 [ # # ]: 0 : segment->abs_rate = ABS (segment->rate);
1711 : :
1712 : : /* save values */
1713 [ # # ]: 0 : if (segment->rate > 0.0)
1714 : 0 : current->start_stop = segment->stop;
1715 : : else
1716 : 0 : current->start_start = segment->start;
1717 : :
1718 [ # # ]: 0 : if (current->format == GST_FORMAT_TIME) {
1719 : 0 : end = current->start + current->amount;
1720 [ # # ]: 0 : if (!current->flush) {
1721 : : /* update the segment clipping regions for non-flushing seeks */
1722 [ # # ]: 0 : if (segment->rate > 0.0) {
1723 : 0 : segment->stop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
1724 : 0 : segment->last_stop = segment->stop;
1725 : : } else {
1726 : : gint64 position;
1727 : :
1728 : 0 : position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
1729 : 0 : segment->time = position;
1730 : 0 : segment->start = position;
1731 : 0 : segment->last_stop = position;
1732 : : }
1733 : : }
1734 : : }
1735 : :
1736 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1737 : : "segment now rate %lf, applied rate %lf, "
1738 : : "format GST_FORMAT_TIME, "
1739 : : "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
1740 : : ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
1741 : : segment->rate, segment->applied_rate, GST_TIME_ARGS (segment->start),
1742 : : GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
1743 : : GST_TIME_ARGS (segment->accum));
1744 : :
1745 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1746 : : GST_TIME_ARGS (current->start));
1747 : :
1748 [ # # ]: 0 : if (current->amount == -1) {
1749 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step amount == -1, stop stepping");
1750 : 0 : current->valid = FALSE;
1751 : : } else {
1752 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
1753 : : "rate: %f", current->amount, gst_format_get_name (current->format),
1754 : : current->rate);
1755 : : }
1756 : 0 : }
1757 : :
1758 : : static void
1759 : 0 : stop_stepping (GstBaseSink * sink, GstSegment * segment,
1760 : : GstStepInfo * current, gint64 rstart, gint64 rstop, gboolean eos)
1761 : : {
1762 : : gint64 stop, position;
1763 : : GstMessage *message;
1764 : :
1765 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step complete");
1766 : :
1767 [ # # ]: 0 : if (segment->rate > 0.0)
1768 : 0 : stop = rstart;
1769 : : else
1770 : 0 : stop = rstop;
1771 : :
1772 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ]
1773 : : "step stop at running_time %" GST_TIME_FORMAT, GST_TIME_ARGS (stop));
1774 : :
1775 [ # # ]: 0 : if (stop == -1)
1776 : 0 : current->duration = current->position;
1777 : : else
1778 : 0 : current->duration = stop - current->start;
1779 : :
1780 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step elapsed running_time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1781 : : GST_TIME_ARGS (current->duration));
1782 : :
1783 : 0 : position = current->start + current->duration;
1784 : :
1785 : : /* now move the segment to the new running time */
1786 : 0 : gst_segment_set_running_time (segment, GST_FORMAT_TIME, position);
1787 : :
1788 [ # # ]: 0 : if (current->flush) {
1789 : : /* and remove the accumulated time we flushed, start time did not change */
1790 : 0 : segment->accum = current->start;
1791 : : } else {
1792 : : /* start time is now the stepped position */
1793 : 0 : gst_element_set_start_time (GST_ELEMENT_CAST (sink), position);
1794 : : }
1795 : :
1796 : : /* restore the previous rate */
1797 : 0 : segment->rate = current->start_rate;
1798 [ # # ]: 0 : segment->abs_rate = ABS (segment->rate);
1799 : :
1800 [ # # ]: 0 : if (segment->rate > 0.0)
1801 : 0 : segment->stop = current->start_stop;
1802 : : else
1803 : 0 : segment->start = current->start_start;
1804 : :
1805 : : /* the clip segment is used for position report in paused... */
1806 : 0 : memcpy (sink->abidata.ABI.clip_segment, segment, sizeof (GstSegment));
1807 : :
1808 : : /* post the step done when we know the stepped duration in TIME */
1809 : 0 : message =
1810 : 0 : gst_message_new_step_done (GST_OBJECT_CAST (sink), current->format,
1811 : : current->amount, current->rate, current->flush, current->intermediate,
1812 : : current->duration, eos);
1813 : 0 : gst_message_set_seqnum (message, current->seqnum);
1814 : 0 : gst_element_post_message (GST_ELEMENT_CAST (sink), message);
1815 : :
1816 [ # # ]: 0 : if (!current->intermediate)
1817 : 0 : sink->need_preroll = current->need_preroll;
1818 : :
1819 : : /* and the current step info finished and becomes invalid */
1820 : 0 : current->valid = FALSE;
1821 : 0 : }
1822 : :
1823 : : static gboolean
1824 : 0 : handle_stepping (GstBaseSink * sink, GstSegment * segment,
1825 : : GstStepInfo * current, gint64 * cstart, gint64 * cstop, gint64 * rstart,
1826 : : gint64 * rstop)
1827 : : {
1828 : 0 : gboolean step_end = FALSE;
1829 : :
1830 : : /* see if we need to skip this buffer because of stepping */
1831 [ # # # ]: 0 : switch (current->format) {
1832 : : case GST_FORMAT_TIME:
1833 : : {
1834 : : guint64 end;
1835 : : gint64 first, last;
1836 : :
1837 [ # # ]: 0 : if (segment->rate > 0.0) {
1838 [ # # ]: 0 : if (segment->stop == *cstop)
1839 : 0 : *rstop = *rstart + current->amount;
1840 : :
1841 : 0 : first = *rstart;
1842 : 0 : last = *rstop;
1843 : : } else {
1844 [ # # ]: 0 : if (segment->start == *cstart)
1845 : 0 : *rstart = *rstop + current->amount;
1846 : :
1847 : 0 : first = *rstop;
1848 : 0 : last = *rstart;
1849 : : }
1850 : :
1851 : 0 : end = current->start + current->amount;
1852 : 0 : current->position = first - current->start;
1853 : :
1854 [ # # ]: 0 : if (G_UNLIKELY (segment->abs_rate != 1.0))
1855 : 0 : current->position /= segment->abs_rate;
1856 : :
1857 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1858 : : "buffer: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1859 : : GST_TIME_ARGS (first), GST_TIME_ARGS (last));
1860 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1861 : : "got time step %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "/%"
1862 : : GST_TIME_FORMAT, GST_TIME_ARGS (current->position),
1863 : : GST_TIME_ARGS (last - current->start),
1864 : : GST_TIME_ARGS (current->amount));
1865 : :
1866 [ # # ][ # # ]: 0 : if ((current->flush && current->position >= current->amount)
1867 [ # # ]: 0 : || last >= end) {
1868 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "step ended, we need clipping");
1869 : 0 : step_end = TRUE;
1870 [ # # ]: 0 : if (segment->rate > 0.0) {
1871 : 0 : *rstart = end;
1872 : 0 : *cstart = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
1873 : : } else {
1874 : 0 : *rstop = end;
1875 : 0 : *cstop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
1876 : : }
1877 : : }
1878 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1879 : : "cstart %" GST_TIME_FORMAT ", rstart %" GST_TIME_FORMAT,
1880 : : GST_TIME_ARGS (*cstart), GST_TIME_ARGS (*rstart));
1881 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1882 : : "cstop %" GST_TIME_FORMAT ", rstop %" GST_TIME_FORMAT,
1883 : : GST_TIME_ARGS (*cstop), GST_TIME_ARGS (*rstop));
1884 : 0 : break;
1885 : : }
1886 : : case GST_FORMAT_BUFFERS:
1887 [ # # ]: 0 : GST_DEBUG_OBJECT (sink,
1888 : : "got default step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1889 : : current->position, current->amount);
1890 : :
1891 [ # # ]: 0 : if (current->position < current->amount) {
1892 : 0 : current->position++;
1893 : : } else {
1894 : 0 : step_end = TRUE;
1895 : : }
1896 : 0 : break;
1897 : : case GST_FORMAT_DEFAULT:
1898 : : default:
1899 [ # # ]: 0 : GST_DEBUG_OBJECT (sink,
1900 : : "got unknown step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1901 : : current->position, current->amount);
1902 : 0 : break;
1903 : : }
1904 : 0 : return step_end;
1905 : : }
1906 : :
1907 : : /* with STREAM_LOCK, PREROLL_LOCK
1908 : : *
1909 : : * Returns TRUE if the object needs synchronisation and takes therefore
1910 : : * part in prerolling.
1911 : : *
1912 : : * rsstart/rsstop contain the start/stop in stream time.
1913 : : * rrstart/rrstop contain the start/stop in running time.
1914 : : */
1915 : : static gboolean
1916 : 686264 : gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
1917 : : GstClockTime * rsstart, GstClockTime * rsstop,
1918 : : GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
1919 : : gboolean * stepped, GstSegment * segment, GstStepInfo * step,
1920 : : gboolean * step_end, guint8 obj_type)
1921 : : {
1922 : : GstBaseSinkClass *bclass;
1923 : : GstBuffer *buffer;
1924 : : GstClockTime start, stop; /* raw start/stop timestamps */
1925 : : gint64 cstart, cstop; /* clipped raw timestamps */
1926 : : gint64 rstart, rstop; /* clipped timestamps converted to running time */
1927 : : GstClockTime sstart, sstop; /* clipped timestamps converted to stream time */
1928 : : GstFormat format;
1929 : : GstBaseSinkPrivate *priv;
1930 : : gboolean eos;
1931 : :
1932 : 686264 : priv = basesink->priv;
1933 : :
1934 : : /* start with nothing */
1935 : 686264 : start = stop = GST_CLOCK_TIME_NONE;
1936 : :
1937 [ + + ]: 686264 : if (G_UNLIKELY (OBJ_IS_EVENT (obj_type))) {
1938 : 192 : GstEvent *event = GST_EVENT_CAST (obj);
1939 : :
1940 [ + + ]: 192 : switch (GST_EVENT_TYPE (event)) {
1941 : : /* EOS event needs syncing */
1942 : : case GST_EVENT_EOS:
1943 : : {
1944 [ + - ]: 37 : if (basesink->segment.rate >= 0.0) {
1945 : 37 : sstart = sstop = priv->current_sstop;
1946 [ + + ]: 37 : if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
1947 : : /* we have not seen a buffer yet, use the segment values */
1948 : 2 : sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
1949 : : basesink->segment.format, basesink->segment.stop);
1950 : : }
1951 : : } else {
1952 : 0 : sstart = sstop = priv->current_sstart;
1953 [ # # ]: 0 : if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
1954 : : /* we have not seen a buffer yet, use the segment values */
1955 : 0 : sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
1956 : : basesink->segment.format, basesink->segment.start);
1957 : : }
1958 : : }
1959 : :
1960 : 37 : rstart = rstop = priv->eos_rtime;
1961 : 37 : *do_sync = rstart != -1;
1962 [ - + ][ # # ]: 37 : GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1963 : : GST_TIME_ARGS (rstart));
1964 : : /* if we are stepping, we end now */
1965 : 37 : *step_end = step->valid;
1966 : 37 : eos = TRUE;
1967 : 37 : goto eos_done;
1968 : : }
1969 : : default:
1970 : : /* other events do not need syncing */
1971 : : /* FIXME, maybe NEWSEGMENT might need synchronisation
1972 : : * since the POSITION query depends on accumulated times and
1973 : : * we cannot accumulate the current segment before the previous
1974 : : * one completed.
1975 : : */
1976 : 155 : return FALSE;
1977 : : }
1978 : : }
1979 : :
1980 : 686072 : eos = FALSE;
1981 : :
1982 : : again:
1983 : : /* else do buffer sync code */
1984 : 686072 : buffer = GST_BUFFER_CAST (obj);
1985 : :
1986 : 686072 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
1987 : :
1988 : : /* just get the times to see if we need syncing, if the start returns -1 we
1989 : : * don't sync. */
1990 [ + ]: 686072 : if (bclass->get_times)
1991 : 686074 : bclass->get_times (basesink, buffer, &start, &stop);
1992 : :
1993 [ + + ]: 686072 : if (!GST_CLOCK_TIME_IS_VALID (start)) {
1994 : : /* we don't need to sync but we still want to get the timestamps for
1995 : : * tracking the position */
1996 : 6 : gst_base_sink_get_times (basesink, buffer, &start, &stop);
1997 : 6 : *do_sync = FALSE;
1998 : : } else {
1999 : 686066 : *do_sync = TRUE;
2000 : : }
2001 : :
2002 [ - + ][ # # ]: 686072 : GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2003 : : ", stop: %" GST_TIME_FORMAT ", do_sync %d", GST_TIME_ARGS (start),
2004 : : GST_TIME_ARGS (stop), *do_sync);
2005 : :
2006 : : /* collect segment and format for code clarity */
2007 : 686074 : format = segment->format;
2008 : :
2009 : : /* no timestamp clipping if we did not get a TIME segment format */
2010 [ + + ]: 686074 : if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
2011 : 686060 : cstart = start;
2012 : 686060 : cstop = stop;
2013 : : /* do running and stream time in TIME format */
2014 : 686060 : format = GST_FORMAT_TIME;
2015 [ - + ]: 686060 : GST_LOG_OBJECT (basesink, "not time format, don't clip");
2016 : 686058 : goto do_times;
2017 : : }
2018 : :
2019 : : /* clip, only when we know about time */
2020 [ - + ]: 14 : if (G_UNLIKELY (!gst_segment_clip (segment, GST_FORMAT_TIME,
2021 : : (gint64) start, (gint64) stop, &cstart, &cstop))) {
2022 [ # # ]: 0 : if (step->valid) {
2023 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "step out of segment");
2024 : : /* when we are stepping, pretend we're at the end of the segment */
2025 [ # # ]: 0 : if (segment->rate > 0.0) {
2026 : 0 : cstart = segment->stop;
2027 : 0 : cstop = segment->stop;
2028 : : } else {
2029 : 0 : cstart = segment->start;
2030 : 0 : cstop = segment->start;
2031 : : }
2032 : 0 : goto do_times;
2033 : : }
2034 : 0 : goto out_of_segment;
2035 : : }
2036 : :
2037 [ + + ][ + + ]: 14 : if (G_UNLIKELY (start != cstart || stop != cstop)) {
2038 [ - + ][ # # ]: 3 : GST_DEBUG_OBJECT (basesink, "clipped to: start %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2039 : : ", stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (cstart),
2040 : : GST_TIME_ARGS (cstop));
2041 : : }
2042 : :
2043 : : /* set last stop position */
2044 [ + - ][ + - ]: 14 : if (G_LIKELY (stop != GST_CLOCK_TIME_NONE && cstop != GST_CLOCK_TIME_NONE))
2045 : 14 : gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstop);
2046 : : else
2047 : 0 : gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstart);
2048 : :
2049 : : do_times:
2050 : 686072 : rstart = gst_segment_to_running_time (segment, format, cstart);
2051 : 686074 : rstop = gst_segment_to_running_time (segment, format, cstop);
2052 : :
2053 [ - + ]: 686074 : if (G_UNLIKELY (step->valid)) {
2054 [ # # ]: 0 : if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop,
2055 : : &rstart, &rstop))) {
2056 : : /* step is still busy, we discard data when we are flushing */
2057 : 0 : *stepped = step->flush;
2058 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "stepping busy");
2059 : : }
2060 : : }
2061 : : /* this can produce wrong values if we accumulated non-TIME segments. If this happens,
2062 : : * upstream is behaving very badly */
2063 : 686074 : sstart = gst_segment_to_stream_time (segment, format, cstart);
2064 : 686073 : sstop = gst_segment_to_stream_time (segment, format, cstop);
2065 : :
2066 : : eos_done:
2067 : : /* eos_done label only called when doing EOS, we also stop stepping then */
2068 [ - + ][ # # ]: 686110 : if (*step_end && step->flush) {
2069 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "flushing step ended");
2070 : 0 : stop_stepping (basesink, segment, step, rstart, rstop, eos);
2071 : 0 : *step_end = FALSE;
2072 : : /* re-determine running start times for adjusted segment
2073 : : * (which has a flushed amount of running/accumulated time removed) */
2074 [ # # ][ # # ]: 0 : if (!GST_IS_EVENT (obj)) {
[ # # ][ - ]
2075 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "refresh sync times");
2076 : 0 : goto again;
2077 : : }
2078 : : }
2079 : :
2080 : : /* save times */
2081 : 686108 : *rsstart = sstart;
2082 : 686108 : *rsstop = sstop;
2083 : 686108 : *rrstart = rstart;
2084 : 686108 : *rrstop = rstop;
2085 : :
2086 : : /* buffers and EOS always need syncing and preroll */
2087 : 686108 : return TRUE;
2088 : :
2089 : : /* special cases */
2090 : : out_of_segment:
2091 : : {
2092 : : /* we usually clip in the chain function already but stepping could cause
2093 : : * the segment to be updated later. we return FALSE so that we don't try
2094 : : * to sync on it. */
2095 [ # # ]: 0 : GST_LOG_OBJECT (basesink, "buffer skipped, not in segment");
2096 : 686263 : return FALSE;
2097 : : }
2098 : : }
2099 : :
2100 : : /* with STREAM_LOCK, PREROLL_LOCK, LOCK
2101 : : * adjust a timestamp with the latency and timestamp offset. This function does
2102 : : * not adjust for the render delay. */
2103 : : static GstClockTime
2104 : 686049 : gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
2105 : : {
2106 : : GstClockTimeDiff ts_offset;
2107 : :
2108 : : /* don't do anything funny with invalid timestamps */
2109 [ - + ]: 686049 : if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time)))
2110 : 0 : return time;
2111 : :
2112 : 686049 : time += basesink->priv->latency;
2113 : :
2114 : : /* apply offset, be carefull for underflows */
2115 : 686049 : ts_offset = basesink->priv->ts_offset;
2116 [ + ]: 686049 : if (ts_offset < 0) {
2117 : 0 : ts_offset = -ts_offset;
2118 [ - ]: 0 : if (ts_offset < time)
2119 : 0 : time -= ts_offset;
2120 : : else
2121 : 0 : time = 0;
2122 : : } else
2123 : 686050 : time += ts_offset;
2124 : :
2125 : : /* subtract the render delay again, which was included in the latency */
2126 [ + + ]: 686049 : if (time > basesink->priv->render_delay)
2127 : 3673 : time -= basesink->priv->render_delay;
2128 : : else
2129 : 682376 : time = 0;
2130 : :
2131 : 686049 : return time;
2132 : : }
2133 : :
2134 : : /**
2135 : : * gst_base_sink_wait_clock:
2136 : : * @sink: the sink
2137 : : * @time: the running_time to be reached
2138 : : * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
2139 : : *
2140 : : * This function will block until @time is reached. It is usually called by
2141 : : * subclasses that use their own internal synchronisation.
2142 : : *
2143 : : * If @time is not valid, no sycnhronisation is done and #GST_CLOCK_BADTIME is
2144 : : * returned. Likewise, if synchronisation is disabled in the element or there
2145 : : * is no clock, no synchronisation is done and #GST_CLOCK_BADTIME is returned.
2146 : : *
2147 : : * This function should only be called with the PREROLL_LOCK held, like when
2148 : : * receiving an EOS event in the #GstBaseSinkClass.event() vmethod or when
2149 : : * receiving a buffer in
2150 : : * the #GstBaseSinkClass.render() vmethod.
2151 : : *
2152 : : * The @time argument should be the running_time of when this method should
2153 : : * return and is not adjusted with any latency or offset configured in the
2154 : : * sink.
2155 : : *
2156 : : * Since: 0.10.20
2157 : : *
2158 : : * Returns: #GstClockReturn
2159 : : */
2160 : : GstClockReturn
2161 : 686046 : gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
2162 : : GstClockTimeDiff * jitter)
2163 : : {
2164 : : GstClockReturn ret;
2165 : : GstClock *clock;
2166 : : GstClockTime base_time;
2167 : :
2168 [ - + ]: 686046 : if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time)))
2169 : 0 : goto invalid_time;
2170 : :
2171 : 686046 : GST_OBJECT_LOCK (sink);
2172 [ + + ]: 686047 : if (G_UNLIKELY (!sink->sync))
2173 : 680811 : goto no_sync;
2174 : :
2175 [ + + ]: 5236 : if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL))
2176 : 4 : goto no_clock;
2177 : :
2178 : 5232 : base_time = GST_ELEMENT_CAST (sink)->base_time;
2179 [ - + ][ # # ]: 5232 : GST_LOG_OBJECT (sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2180 : : "time %" GST_TIME_FORMAT ", base_time %" GST_TIME_FORMAT,
2181 : : GST_TIME_ARGS (time), GST_TIME_ARGS (base_time));
2182 : :
2183 : : /* add base_time to running_time to get the time against the clock */
2184 : 5232 : time += base_time;
2185 : :
2186 : : /* Re-use existing clockid if available */
2187 [ + + ]: 5232 : if (G_LIKELY (sink->priv->cached_clock_id != NULL)) {
2188 [ - + ]: 5225 : if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
2189 : : time)) {
2190 : 0 : gst_clock_id_unref (sink->priv->cached_clock_id);
2191 : 0 : sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
2192 : : }
2193 : : } else
2194 : 7 : sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
2195 : 5232 : GST_OBJECT_UNLOCK (sink);
2196 : :
2197 : : /* A blocking wait is performed on the clock. We save the ClockID
2198 : : * so we can unlock the entry at any time. While we are blocking, we
2199 : : * release the PREROLL_LOCK so that other threads can interrupt the
2200 : : * entry. */
2201 : 5232 : sink->clock_id = sink->priv->cached_clock_id;
2202 : : /* release the preroll lock while waiting */
2203 : 5232 : GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
2204 : :
2205 : 5232 : ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);
2206 : :
2207 : 5232 : GST_PAD_PREROLL_LOCK (sink->sinkpad);
2208 : 5232 : sink->clock_id = NULL;
2209 : :
2210 : 5232 : return ret;
2211 : :
2212 : : /* no syncing needed */
2213 : : invalid_time:
2214 : : {
2215 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "time not valid, no sync needed");
2216 : 0 : return GST_CLOCK_BADTIME;
2217 : : }
2218 : : no_sync:
2219 : : {
2220 [ - + ]: 680811 : GST_DEBUG_OBJECT (sink, "sync disabled");
2221 : 680811 : GST_OBJECT_UNLOCK (sink);
2222 : 680813 : return GST_CLOCK_BADTIME;
2223 : : }
2224 : : no_clock:
2225 : : {
2226 [ - + ]: 4 : GST_DEBUG_OBJECT (sink, "no clock, can't sync");
2227 : 4 : GST_OBJECT_UNLOCK (sink);
2228 : 686049 : return GST_CLOCK_BADTIME;
2229 : : }
2230 : : }
2231 : :
2232 : : /**
2233 : : * gst_base_sink_wait_preroll:
2234 : : * @sink: the sink
2235 : : *
2236 : : * If the #GstBaseSinkClass.render() method performs its own synchronisation
2237 : : * against the clock it must unblock when going from PLAYING to the PAUSED state
2238 : : * and call this method before continuing to render the remaining data.
2239 : : *
2240 : : * This function will block until a state change to PLAYING happens (in which
2241 : : * case this function returns #GST_FLOW_OK) or the processing must be stopped due
2242 : : * to a state change to READY or a FLUSH event (in which case this function
2243 : : * returns #GST_FLOW_WRONG_STATE).
2244 : : *
2245 : : * This function should only be called with the PREROLL_LOCK held, like in the
2246 : : * render function.
2247 : : *
2248 : : * Returns: #GST_FLOW_OK if the preroll completed and processing can
2249 : : * continue. Any other return value should be returned from the render vmethod.
2250 : : *
2251 : : * Since: 0.10.11
2252 : : */
2253 : : GstFlowReturn
2254 : 173 : gst_base_sink_wait_preroll (GstBaseSink * sink)
2255 : : {
2256 : 173 : sink->have_preroll = TRUE;
2257 [ - + ]: 173 : GST_DEBUG_OBJECT (sink, "waiting in preroll for flush or PLAYING");
2258 : : /* block until the state changes, or we get a flush, or something */
2259 : 173 : GST_PAD_PREROLL_WAIT (sink->sinkpad);
2260 : 173 : sink->have_preroll = FALSE;
2261 [ + + ]: 173 : if (G_UNLIKELY (sink->flushing))
2262 : 63 : goto stopping;
2263 [ - + ]: 110 : if (G_UNLIKELY (sink->priv->step_unlock))
2264 : 0 : goto step_unlocked;
2265 [ - + ]: 110 : GST_DEBUG_OBJECT (sink, "continue after preroll");
2266 : :
2267 : 110 : return GST_FLOW_OK;
2268 : :
2269 : : /* ERRORS */
2270 : : stopping:
2271 : : {
2272 [ - + ]: 63 : GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush");
2273 : 63 : return GST_FLOW_WRONG_STATE;
2274 : : }
2275 : : step_unlocked:
2276 : : {
2277 : 0 : sink->priv->step_unlock = FALSE;
2278 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "preroll interrupted because of step");
2279 : 173 : return GST_FLOW_STEP;
2280 : : }
2281 : : }
2282 : :
2283 : : static inline guint8
2284 : 365 : get_object_type (GstMiniObject * obj)
2285 : : {
2286 : : guint8 obj_type;
2287 : :
2288 [ - + ][ + - ]: 365 : if (G_LIKELY (GST_IS_BUFFER (obj)))
[ + + ][ + + ]
2289 : 213 : obj_type = _PR_IS_BUFFER;
2290 [ - + ][ + - ]: 152 : else if (GST_IS_EVENT (obj))
[ + - ][ + - ]
2291 : 152 : obj_type = _PR_IS_EVENT;
2292 [ # # ][ # # ]: 0 : else if (GST_IS_BUFFER_LIST (obj))
[ # # ][ # # ]
2293 : 0 : obj_type = _PR_IS_BUFFERLIST;
2294 : : else
2295 : 0 : obj_type = _PR_IS_NOTHING;
2296 : :
2297 : 365 : return obj_type;
2298 : : }
2299 : :
2300 : : /**
2301 : : * gst_base_sink_do_preroll:
2302 : : * @sink: the sink
2303 : : * @obj: (transfer none): the mini object that caused the preroll
2304 : : *
2305 : : * If the @sink spawns its own thread for pulling buffers from upstream it
2306 : : * should call this method after it has pulled a buffer. If the element needed
2307 : : * to preroll, this function will perform the preroll and will then block
2308 : : * until the element state is changed.
2309 : : *
2310 : : * This function should be called with the PREROLL_LOCK held.
2311 : : *
2312 : : * Returns: #GST_FLOW_OK if the preroll completed and processing can
2313 : : * continue. Any other return value should be returned from the render vmethod.
2314 : : *
2315 : : * Since: 0.10.22
2316 : : */
2317 : : GstFlowReturn
2318 : 686119 : gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
2319 : : {
2320 : : GstFlowReturn ret;
2321 : :
2322 [ + + ]: 686229 : while (G_UNLIKELY (sink->need_preroll)) {
2323 : : guint8 obj_type;
2324 [ - + ]: 173 : GST_DEBUG_OBJECT (sink, "prerolling object %p", obj);
2325 : :
2326 : 173 : obj_type = get_object_type (obj);
2327 : :
2328 : 173 : ret = gst_base_sink_preroll_object (sink, obj_type, obj);
2329 [ - + ]: 173 : if (ret != GST_FLOW_OK)
2330 : 0 : goto preroll_failed;
2331 : :
2332 : : /* need to recheck here because the commit state could have
2333 : : * made us not need the preroll anymore */
2334 [ + - ]: 173 : if (G_LIKELY (sink->need_preroll)) {
2335 : : /* block until the state changes, or we get a flush, or something */
2336 : 173 : ret = gst_base_sink_wait_preroll (sink);
2337 [ + + ][ + - ]: 173 : if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
2338 : 63 : goto preroll_failed;
2339 : : }
2340 : : }
2341 : 686056 : return GST_FLOW_OK;
2342 : :
2343 : : /* ERRORS */
2344 : : preroll_failed:
2345 : : {
2346 [ - + ]: 63 : GST_DEBUG_OBJECT (sink, "preroll failed: %s", gst_flow_get_name (ret));
2347 : 686119 : return ret;
2348 : : }
2349 : : }
2350 : :
2351 : : /**
2352 : : * gst_base_sink_wait_eos:
2353 : : * @sink: the sink
2354 : : * @time: the running_time to be reached
2355 : : * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
2356 : : *
2357 : : * This function will block until @time is reached. It is usually called by
2358 : : * subclasses that use their own internal synchronisation but want to let the
2359 : : * EOS be handled by the base class.
2360 : : *
2361 : : * This function should only be called with the PREROLL_LOCK held, like when
2362 : : * receiving an EOS event in the ::event vmethod.
2363 : : *
2364 : : * The @time argument should be the running_time of when the EOS should happen
2365 : : * and will be adjusted with any latency and offset configured in the sink.
2366 : : *
2367 : : * Returns: #GstFlowReturn
2368 : : *
2369 : : * Since: 0.10.15
2370 : : */
2371 : : GstFlowReturn
2372 : 0 : gst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time,
2373 : : GstClockTimeDiff * jitter)
2374 : : {
2375 : : GstClockReturn status;
2376 : : GstFlowReturn ret;
2377 : :
2378 : : do {
2379 : : GstClockTime stime;
2380 : :
2381 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "checking preroll");
2382 : :
2383 : : /* first wait for the playing state before we can continue */
2384 [ # # ]: 0 : while (G_UNLIKELY (sink->need_preroll)) {
2385 : 0 : ret = gst_base_sink_wait_preroll (sink);
2386 [ # # ][ # # ]: 0 : if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
2387 : 0 : goto flushing;
2388 : : }
2389 : :
2390 : : /* preroll done, we can sync since we are in PLAYING now. */
2391 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
[ # # ][ # # ]
[ # # ]
2392 : : GST_TIME_FORMAT, GST_TIME_ARGS (time));
2393 : :
2394 : : /* compensate for latency and ts_offset. We don't adjust for render delay
2395 : : * because we don't interact with the device on EOS normally. */
2396 : 0 : stime = gst_base_sink_adjust_time (sink, time);
2397 : :
2398 : : /* wait for the clock, this can be interrupted because we got shut down or
2399 : : * we PAUSED. */
2400 : 0 : status = gst_base_sink_wait_clock (sink, stime, jitter);
2401 : :
2402 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "clock returned %d", status);
2403 : :
2404 : : /* invalid time, no clock or sync disabled, just continue then */
2405 [ # # ]: 0 : if (status == GST_CLOCK_BADTIME)
2406 : 0 : break;
2407 : :
2408 : : /* waiting could have been interrupted and we can be flushing now */
2409 [ # # ]: 0 : if (G_UNLIKELY (sink->flushing))
2410 : 0 : goto flushing;
2411 : :
2412 : : /* retry if we got unscheduled, which means we did not reach the timeout
2413 : : * yet. if some other error occures, we continue. */
2414 [ # # ]: 0 : } while (status == GST_CLOCK_UNSCHEDULED);
2415 : :
2416 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "end of stream");
2417 : :
2418 : 0 : return GST_FLOW_OK;
2419 : :
2420 : : /* ERRORS */
2421 : : flushing:
2422 : : {
2423 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "we are flushing");
2424 : 0 : return GST_FLOW_WRONG_STATE;
2425 : : }
2426 : : }
2427 : :
2428 : : /* with STREAM_LOCK, PREROLL_LOCK
2429 : : *
2430 : : * Make sure we are in PLAYING and synchronize an object to the clock.
2431 : : *
2432 : : * If we need preroll, we are not in PLAYING. We try to commit the state
2433 : : * if needed and then block if we still are not PLAYING.
2434 : : *
2435 : : * We start waiting on the clock in PLAYING. If we got interrupted, we
2436 : : * immediatly try to re-preroll.
2437 : : *
2438 : : * Some objects do not need synchronisation (most events) and so this function
2439 : : * immediatly returns GST_FLOW_OK.
2440 : : *
2441 : : * for objects that arrive later than max-lateness to be synchronized to the
2442 : : * clock have the @late boolean set to TRUE.
2443 : : *
2444 : : * This function keeps a running average of the jitter (the diff between the
2445 : : * clock time and the requested sync time). The jitter is negative for
2446 : : * objects that arrive in time and positive for late buffers.
2447 : : *
2448 : : * does not take ownership of obj.
2449 : : */
2450 : : static GstFlowReturn
2451 : 686264 : gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,
2452 : : GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)
2453 : : {
2454 : 686264 : GstClockTimeDiff jitter = 0;
2455 : : gboolean syncable;
2456 : 686264 : GstClockReturn status = GST_CLOCK_OK;
2457 : : GstClockTime rstart, rstop, sstart, sstop, stime;
2458 : : gboolean do_sync;
2459 : : GstBaseSinkPrivate *priv;
2460 : : GstFlowReturn ret;
2461 : : GstStepInfo *current, *pending;
2462 : : gboolean stepped;
2463 : :
2464 : 686264 : priv = basesink->priv;
2465 : :
2466 : : do_step:
2467 : 686264 : sstart = sstop = rstart = rstop = GST_CLOCK_TIME_NONE;
2468 : 686264 : do_sync = TRUE;
2469 : 686264 : stepped = FALSE;
2470 : :
2471 : 686264 : priv->current_rstart = GST_CLOCK_TIME_NONE;
2472 : :
2473 : : /* get stepping info */
2474 : 686264 : current = &priv->current_step;
2475 : 686264 : pending = &priv->pending_step;
2476 : :
2477 : : /* get timing information for this object against the render segment */
2478 : 686264 : syncable = gst_base_sink_get_sync_times (basesink, obj,
2479 : : &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, &basesink->segment,
2480 : : current, step_end, obj_type);
2481 : :
2482 [ - + ]: 686267 : if (G_UNLIKELY (stepped))
2483 : 0 : goto step_skipped;
2484 : :
2485 : : /* a syncable object needs to participate in preroll and
2486 : : * clocking. All buffers and EOS are syncable. */
2487 [ + + ]: 686267 : if (G_UNLIKELY (!syncable))
2488 : 155 : goto not_syncable;
2489 : :
2490 : : /* store timing info for current object */
2491 : 686112 : priv->current_rstart = rstart;
2492 [ + + ]: 686112 : priv->current_rstop = (GST_CLOCK_TIME_IS_VALID (rstop) ? rstop : rstart);
2493 : :
2494 : : /* save sync time for eos when the previous object needed sync */
2495 [ + + ]: 686112 : priv->eos_rtime = (do_sync ? priv->current_rstop : GST_CLOCK_TIME_NONE);
2496 : :
2497 : : /* calculate inter frame spacing */
2498 [ + + ][ + + ]: 686112 : if (G_UNLIKELY (priv->prev_rstart != -1 && priv->prev_rstart < rstart)) {
2499 : : GstClockTime in_diff;
2500 : :
2501 : 3648 : in_diff = rstart - priv->prev_rstart;
2502 : :
2503 [ + + ]: 3648 : if (priv->avg_in_diff == -1)
2504 : 17 : priv->avg_in_diff = in_diff;
2505 : : else
2506 : 3631 : priv->avg_in_diff = UPDATE_RUNNING_AVG (priv->avg_in_diff, in_diff);
2507 : :
2508 [ - + ][ # # ]: 3648 : GST_LOG_OBJECT (basesink, "avg frame diff %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
2509 : : GST_TIME_ARGS (priv->avg_in_diff));
2510 : :
2511 : : }
2512 : 686111 : priv->prev_rstart = rstart;
2513 : :
2514 [ - + ][ # # ]: 686111 : if (G_UNLIKELY (priv->earliest_in_time != -1
2515 : : && rstart < priv->earliest_in_time))
2516 : 0 : goto qos_dropped;
2517 : :
2518 : : again:
2519 : : /* first do preroll, this makes sure we commit our state
2520 : : * to PAUSED and can continue to PLAYING. We cannot perform
2521 : : * any clock sync in PAUSED because there is no clock. */
2522 : 686120 : ret = gst_base_sink_do_preroll (basesink, obj);
2523 [ + + ]: 686119 : if (G_UNLIKELY (ret != GST_FLOW_OK))
2524 : 63 : goto preroll_failed;
2525 : :
2526 : : /* update the segment with a pending step if the current one is invalid and we
2527 : : * have a new pending one. We only accept new step updates after a preroll */
2528 [ - + ][ # # ]: 686056 : if (G_UNLIKELY (pending->valid && !current->valid)) {
2529 : 0 : start_stepping (basesink, &basesink->segment, pending, current);
2530 : 0 : goto do_step;
2531 : : }
2532 : :
2533 : : /* After rendering we store the position of the last buffer so that we can use
2534 : : * it to report the position. We need to take the lock here. */
2535 : 686056 : GST_OBJECT_LOCK (basesink);
2536 : 686057 : priv->current_sstart = sstart;
2537 [ + + ]: 686057 : priv->current_sstop = (GST_CLOCK_TIME_IS_VALID (sstop) ? sstop : sstart);
2538 : 686057 : GST_OBJECT_UNLOCK (basesink);
2539 : :
2540 [ + + ]: 686058 : if (!do_sync)
2541 : 8 : goto done;
2542 : :
2543 : : /* adjust for latency */
2544 : 686050 : stime = gst_base_sink_adjust_time (basesink, rstart);
2545 : :
2546 : : /* adjust for render-delay, avoid underflows */
2547 [ + ]: 686047 : if (GST_CLOCK_TIME_IS_VALID (stime)) {
2548 [ + + ]: 686048 : if (stime > priv->render_delay)
2549 : 3674 : stime -= priv->render_delay;
2550 : : else
2551 : 682374 : stime = 0;
2552 : : }
2553 : :
2554 : : /* preroll done, we can sync since we are in PLAYING now. */
2555 [ - + ][ # # ]: 686047 : GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2556 : : GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
2557 : : GST_TIME_ARGS (rstart), GST_TIME_ARGS (stime));
2558 : :
2559 : : /* This function will return immediatly if start == -1, no clock
2560 : : * or sync is disabled with GST_CLOCK_BADTIME. */
2561 : 686047 : status = gst_base_sink_wait_clock (basesink, stime, &jitter);
2562 : :
2563 [ - + ][ # # ]: 686010 : GST_DEBUG_OBJECT (basesink, "clock returned %d, jitter %c%" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ][ # # ]
2564 : : status, (jitter < 0 ? '-' : ' '), GST_TIME_ARGS (ABS (jitter)));
2565 : :
2566 : : /* invalid time, no clock or sync disabled, just render */
2567 [ + + ]: 686049 : if (status == GST_CLOCK_BADTIME)
2568 : 680817 : goto done;
2569 : :
2570 : : /* waiting could have been interrupted and we can be flushing now */
2571 [ + + ]: 5232 : if (G_UNLIKELY (basesink->flushing))
2572 : 1 : goto flushing;
2573 : :
2574 : : /* check for unlocked by a state change, we are not flushing so
2575 : : * we can try to preroll on the current buffer. */
2576 [ + + ]: 5231 : if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
2577 [ - + ]: 9 : GST_DEBUG_OBJECT (basesink, "unscheduled, waiting some more");
2578 : 9 : priv->call_preroll = TRUE;
2579 : 9 : goto again;
2580 : : }
2581 : :
2582 : : /* successful syncing done, record observation */
2583 : 5222 : priv->current_jitter = jitter;
2584 : :
2585 : : /* check if the object should be dropped */
2586 : 5222 : *late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
2587 : : status, jitter);
2588 : :
2589 : : done:
2590 : 686047 : return GST_FLOW_OK;
2591 : :
2592 : : /* ERRORS */
2593 : : step_skipped:
2594 : : {
2595 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "skipped stepped object %p", obj);
2596 : 0 : *late = TRUE;
2597 : 0 : return GST_FLOW_OK;
2598 : : }
2599 : : not_syncable:
2600 : : {
2601 [ - + ]: 155 : GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj);
2602 : 155 : return GST_FLOW_OK;
2603 : : }
2604 : : qos_dropped:
2605 : : {
2606 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "dropped because of QoS %p", obj);
2607 : 0 : *late = TRUE;
2608 : 0 : return GST_FLOW_OK;
2609 : : }
2610 : : flushing:
2611 : : {
2612 [ - + ]: 1 : GST_DEBUG_OBJECT (basesink, "we are flushing");
2613 : 1 : return GST_FLOW_WRONG_STATE;
2614 : : }
2615 : : preroll_failed:
2616 : : {
2617 [ - + ]: 63 : GST_DEBUG_OBJECT (basesink, "preroll failed");
2618 : 63 : *step_end = FALSE;
2619 : 686266 : return ret;
2620 : : }
2621 : : }
2622 : :
2623 : : static gboolean
2624 : 0 : gst_base_sink_send_qos (GstBaseSink * basesink, GstQOSType type,
2625 : : gdouble proportion, GstClockTime time, GstClockTimeDiff diff)
2626 : : {
2627 : : GstEvent *event;
2628 : : gboolean res;
2629 : :
2630 : : /* generate Quality-of-Service event */
2631 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
[ # # ][ # # ]
[ # # ]
2632 : : "qos: type %d, proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %"
2633 : : GST_TIME_FORMAT, type, proportion, diff, GST_TIME_ARGS (time));
2634 : :
2635 : 0 : event = gst_event_new_qos_full (type, proportion, diff, time);
2636 : :
2637 : : /* send upstream */
2638 : 0 : res = gst_pad_push_event (basesink->sinkpad, event);
2639 : :
2640 : 0 : return res;
2641 : : }
2642 : :
2643 : : static void
2644 : 686242 : gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped)
2645 : : {
2646 : : GstBaseSinkPrivate *priv;
2647 : : GstClockTime start, stop;
2648 : : GstClockTimeDiff jitter;
2649 : : GstClockTime pt, entered, left;
2650 : : GstClockTime duration;
2651 : : gdouble rate;
2652 : :
2653 : 686242 : priv = sink->priv;
2654 : :
2655 : 686242 : start = priv->current_rstart;
2656 : :
2657 [ - + ]: 686242 : if (priv->current_step.valid)
2658 : 0 : return;
2659 : :
2660 : : /* if Quality-of-Service disabled, do nothing */
2661 [ - + ][ # # ]: 686242 : if (!g_atomic_int_get (&priv->qos_enabled) ||
2662 : : !GST_CLOCK_TIME_IS_VALID (start))
2663 : 686267 : return;
2664 : :
2665 : 0 : stop = priv->current_rstop;
2666 : 0 : jitter = priv->current_jitter;
2667 : :
2668 [ # # ]: 0 : if (jitter < 0) {
2669 : : /* this is the time the buffer entered the sink */
2670 [ # # ]: 0 : if (start < -jitter)
2671 : 0 : entered = 0;
2672 : : else
2673 : 0 : entered = start + jitter;
2674 : 0 : left = start;
2675 : : } else {
2676 : : /* this is the time the buffer entered the sink */
2677 : 0 : entered = start + jitter;
2678 : : /* this is the time the buffer left the sink */
2679 : 0 : left = start + jitter;
2680 : : }
2681 : :
2682 : : /* calculate duration of the buffer */
2683 [ # # ][ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (stop) && stop != start)
2684 : 0 : duration = stop - start;
2685 : : else
2686 : 0 : duration = priv->avg_in_diff;
2687 : :
2688 : : /* if we have the time when the last buffer left us, calculate
2689 : : * processing time */
2690 [ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
2691 [ # # ]: 0 : if (entered > priv->last_left) {
2692 : 0 : pt = entered - priv->last_left;
2693 : : } else {
2694 : 0 : pt = 0;
2695 : : }
2696 : : } else {
2697 : 0 : pt = priv->avg_pt;
2698 : : }
2699 : :
2700 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "start: %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2701 : : ", stop %" GST_TIME_FORMAT ", entered %" GST_TIME_FORMAT ", left %"
2702 : : GST_TIME_FORMAT ", pt: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
2703 : : ",jitter %" G_GINT64_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
2704 : : GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt),
2705 : : GST_TIME_ARGS (duration), jitter);
2706 : :
2707 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "avg_duration: %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2708 : : ", avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
2709 : : GST_TIME_ARGS (priv->avg_duration), GST_TIME_ARGS (priv->avg_pt),
2710 : : priv->avg_rate);
2711 : :
2712 : : /* collect running averages. for first observations, we copy the
2713 : : * values */
2714 [ # # ]: 0 : if (!GST_CLOCK_TIME_IS_VALID (priv->avg_duration))
2715 : 0 : priv->avg_duration = duration;
2716 : : else
2717 : 0 : priv->avg_duration = UPDATE_RUNNING_AVG (priv->avg_duration, duration);
2718 : :
2719 [ # # ]: 0 : if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt))
2720 : 0 : priv->avg_pt = pt;
2721 : : else
2722 : 0 : priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt);
2723 : :
2724 [ # # ]: 0 : if (priv->avg_duration != 0)
2725 : 0 : rate =
2726 : 0 : gst_guint64_to_gdouble (priv->avg_pt) /
2727 : 0 : gst_guint64_to_gdouble (priv->avg_duration);
2728 : : else
2729 : 0 : rate = 1.0;
2730 : :
2731 [ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
2732 [ # # ][ # # ]: 0 : if (dropped || priv->avg_rate < 0.0) {
2733 : 0 : priv->avg_rate = rate;
2734 : : } else {
2735 [ # # ]: 0 : if (rate > 1.0)
2736 : 0 : priv->avg_rate = UPDATE_RUNNING_AVG_N (priv->avg_rate, rate);
2737 : : else
2738 : 0 : priv->avg_rate = UPDATE_RUNNING_AVG_P (priv->avg_rate, rate);
2739 : : }
2740 : : }
2741 : :
2742 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2743 : : "updated: avg_duration: %" GST_TIME_FORMAT ", avg_pt: %" GST_TIME_FORMAT
2744 : : ", avg_rate: %g", GST_TIME_ARGS (priv->avg_duration),
2745 : : GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
2746 : :
2747 : :
2748 [ # # ]: 0 : if (priv->avg_rate >= 0.0) {
2749 : : GstQOSType type;
2750 : : GstClockTimeDiff diff;
2751 : :
2752 : : /* if we have a valid rate, start sending QoS messages */
2753 [ # # ]: 0 : if (priv->current_jitter < 0) {
2754 : : /* make sure we never go below 0 when adding the jitter to the
2755 : : * timestamp. */
2756 [ # # ]: 0 : if (priv->current_rstart < -priv->current_jitter)
2757 : 0 : priv->current_jitter = -priv->current_rstart;
2758 : : }
2759 : :
2760 [ # # ]: 0 : if (priv->throttle_time > 0) {
2761 : 0 : diff = priv->throttle_time;
2762 : 0 : type = GST_QOS_TYPE_THROTTLE;
2763 : : } else {
2764 : 0 : diff = priv->current_jitter;
2765 [ # # ]: 0 : if (diff <= 0)
2766 : 0 : type = GST_QOS_TYPE_OVERFLOW;
2767 : : else
2768 : 0 : type = GST_QOS_TYPE_UNDERFLOW;
2769 : : }
2770 : :
2771 : 0 : gst_base_sink_send_qos (sink, type, priv->avg_rate, priv->current_rstart,
2772 : : diff);
2773 : : }
2774 : :
2775 : : /* record when this buffer will leave us */
2776 : 686267 : priv->last_left = left;
2777 : : }
2778 : :
2779 : : /* reset all qos measuring */
2780 : : static void
2781 : 543 : gst_base_sink_reset_qos (GstBaseSink * sink)
2782 : : {
2783 : : GstBaseSinkPrivate *priv;
2784 : :
2785 : 543 : priv = sink->priv;
2786 : :
2787 : 543 : priv->last_render_time = GST_CLOCK_TIME_NONE;
2788 : 543 : priv->prev_rstart = GST_CLOCK_TIME_NONE;
2789 : 543 : priv->earliest_in_time = GST_CLOCK_TIME_NONE;
2790 : 543 : priv->last_left = GST_CLOCK_TIME_NONE;
2791 : 543 : priv->avg_duration = GST_CLOCK_TIME_NONE;
2792 : 543 : priv->avg_pt = GST_CLOCK_TIME_NONE;
2793 : 543 : priv->avg_rate = -1.0;
2794 : 543 : priv->avg_render = GST_CLOCK_TIME_NONE;
2795 : 543 : priv->avg_in_diff = GST_CLOCK_TIME_NONE;
2796 : 543 : priv->rendered = 0;
2797 : 543 : priv->dropped = 0;
2798 : :
2799 : 543 : }
2800 : :
2801 : : /* Checks if the object was scheduled too late.
2802 : : *
2803 : : * rstart/rstop contain the running_time start and stop values
2804 : : * of the object.
2805 : : *
2806 : : * status and jitter contain the return values from the clock wait.
2807 : : *
2808 : : * returns TRUE if the buffer was too late.
2809 : : */
2810 : : static gboolean
2811 : 5222 : gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
2812 : : GstClockTime rstart, GstClockTime rstop,
2813 : : GstClockReturn status, GstClockTimeDiff jitter)
2814 : : {
2815 : : gboolean late;
2816 : : gint64 max_lateness;
2817 : : GstBaseSinkPrivate *priv;
2818 : :
2819 : 5222 : priv = basesink->priv;
2820 : :
2821 : 5222 : late = FALSE;
2822 : :
2823 : : /* only for objects that were too late */
2824 [ + + ]: 5222 : if (G_LIKELY (status != GST_CLOCK_EARLY))
2825 : 218 : goto in_time;
2826 : :
2827 : 5004 : max_lateness = basesink->abidata.ABI.max_lateness;
2828 : :
2829 : : /* check if frame dropping is enabled */
2830 [ + - ]: 5004 : if (max_lateness == -1)
2831 : 5004 : goto no_drop;
2832 : :
2833 : : /* only check for buffers */
2834 [ # # ][ # # ]: 0 : if (G_UNLIKELY (!GST_IS_BUFFER (obj)))
[ # # ][ # # ]
2835 : 0 : goto not_buffer;
2836 : :
2837 : : /* can't do check if we don't have a timestamp */
2838 [ # # ]: 0 : if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rstart)))
2839 : 0 : goto no_timestamp;
2840 : :
2841 : : /* we can add a valid stop time */
2842 [ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (rstop))
2843 : 0 : max_lateness += rstop;
2844 : : else {
2845 : 0 : max_lateness += rstart;
2846 : : /* no stop time, use avg frame diff */
2847 [ # # ]: 0 : if (priv->avg_in_diff != -1)
2848 : 0 : max_lateness += priv->avg_in_diff;
2849 : : }
2850 : :
2851 : : /* if the jitter bigger than duration and lateness we are too late */
2852 [ # # ]: 0 : if ((late = rstart + jitter > max_lateness)) {
2853 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
2854 : : "buffer is too late %" GST_TIME_FORMAT
2855 : : " > %" GST_TIME_FORMAT, GST_TIME_ARGS (rstart + jitter),
2856 : : GST_TIME_ARGS (max_lateness));
2857 : : /* !!emergency!!, if we did not receive anything valid for more than a
2858 : : * second, render it anyway so the user sees something */
2859 [ # # ][ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (priv->last_render_time) &&
2860 : 0 : rstart - priv->last_render_time > GST_SECOND) {
2861 : 0 : late = FALSE;
2862 [ # # ][ # # ]: 0 : GST_ELEMENT_WARNING (basesink, CORE, CLOCK,
[ # # ][ # # ]
2863 : : (_("A lot of buffers are being dropped.")),
2864 : : ("There may be a timestamping problem, or this computer is too slow."));
2865 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
[ # # ][ # # ]
[ # # ]
2866 : : "**emergency** last buffer at %" GST_TIME_FORMAT " > GST_SECOND",
2867 : : GST_TIME_ARGS (priv->last_render_time));
2868 : : }
2869 : : }
2870 : :
2871 : : done:
2872 [ - + ][ # # ]: 5222 : if (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time)) {
2873 : 5222 : priv->last_render_time = rstart;
2874 : : /* the next allowed input timestamp */
2875 [ - + ]: 5222 : if (priv->throttle_time > 0)
2876 : 0 : priv->earliest_in_time = rstart + priv->throttle_time;
2877 : : }
2878 : 5222 : return late;
2879 : :
2880 : : /* all is fine */
2881 : : in_time:
2882 : : {
2883 [ - + ]: 218 : GST_DEBUG_OBJECT (basesink, "object was scheduled in time");
2884 : 218 : goto done;
2885 : : }
2886 : : no_drop:
2887 : : {
2888 [ - + ]: 5004 : GST_DEBUG_OBJECT (basesink, "frame dropping disabled");
2889 : 5004 : goto done;
2890 : : }
2891 : : not_buffer:
2892 : : {
2893 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "object is not a buffer");
2894 : 0 : return FALSE;
2895 : : }
2896 : : no_timestamp:
2897 : : {
2898 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "buffer has no timestamp");
2899 : 5222 : return FALSE;
2900 : : }
2901 : : }
2902 : :
2903 : : /* called before and after calling the render vmethod. It keeps track of how
2904 : : * much time was spent in the render method and is used to check if we are
2905 : : * flooded */
2906 : : static void
2907 : 0 : gst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start)
2908 : : {
2909 : : GstBaseSinkPrivate *priv;
2910 : :
2911 : 0 : priv = basesink->priv;
2912 : :
2913 [ # # ]: 0 : if (start) {
2914 : 0 : priv->start = gst_util_get_timestamp ();
2915 : : } else {
2916 : : GstClockTime elapsed;
2917 : :
2918 : 0 : priv->stop = gst_util_get_timestamp ();
2919 : :
2920 : 0 : elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
2921 : :
2922 [ # # ]: 0 : if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
2923 : 0 : priv->avg_render = elapsed;
2924 : : else
2925 : 0 : priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
2926 : :
2927 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
[ # # ][ # # ]
[ # # ]
2928 : : "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
2929 : : }
2930 : 0 : }
2931 : :
2932 : : /* with STREAM_LOCK, PREROLL_LOCK,
2933 : : *
2934 : : * Synchronize the object on the clock and then render it.
2935 : : *
2936 : : * takes ownership of obj.
2937 : : */
2938 : : static GstFlowReturn
2939 : 686266 : gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,
2940 : : guint8 obj_type, gpointer obj)
2941 : : {
2942 : : GstFlowReturn ret;
2943 : : GstBaseSinkClass *bclass;
2944 : : gboolean late, step_end;
2945 : : gpointer sync_obj;
2946 : : GstBaseSinkPrivate *priv;
2947 : :
2948 : 686266 : priv = basesink->priv;
2949 : :
2950 [ - + ]: 686266 : if (OBJ_IS_BUFFERLIST (obj_type)) {
2951 : : /*
2952 : : * If buffer list, use the first group buffer within the list
2953 : : * for syncing
2954 : : */
2955 : 0 : sync_obj = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
2956 [ # # ]: 0 : g_assert (NULL != sync_obj);
2957 : : } else {
2958 : 686266 : sync_obj = obj;
2959 : : }
2960 : :
2961 : : again:
2962 : 686266 : late = FALSE;
2963 : 686266 : step_end = FALSE;
2964 : :
2965 : : /* synchronize this object, non syncable objects return OK
2966 : : * immediatly. */
2967 : 686266 : ret =
2968 : 686266 : gst_base_sink_do_sync (basesink, pad, sync_obj, &late, &step_end,
2969 : : obj_type);
2970 [ + + ]: 686265 : if (G_UNLIKELY (ret != GST_FLOW_OK))
2971 : 64 : goto sync_failed;
2972 : :
2973 : : /* and now render, event or buffer/buffer list. */
2974 [ + + ]: 686201 : if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type))) {
2975 : : /* drop late buffers unconditionally, let's hope it's unlikely */
2976 [ - + ]: 686010 : if (G_UNLIKELY (late))
2977 : 0 : goto dropped;
2978 : :
2979 : 686010 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
2980 : :
2981 [ - + ][ # # ]: 686010 : if (G_LIKELY ((OBJ_IS_BUFFERLIST (obj_type) && bclass->render_list) ||
[ + - ][ + - ]
2982 : : (!OBJ_IS_BUFFERLIST (obj_type) && bclass->render))) {
2983 : : gint do_qos;
2984 : :
2985 : : /* read once, to get same value before and after */
2986 : 686010 : do_qos = g_atomic_int_get (&priv->qos_enabled);
2987 : :
2988 [ - + ]: 686011 : GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
2989 : :
2990 : : /* record rendering time for QoS and stats */
2991 [ - + ]: 686011 : if (do_qos)
2992 : 0 : gst_base_sink_do_render_stats (basesink, TRUE);
2993 : :
2994 [ + - ]: 686011 : if (!OBJ_IS_BUFFERLIST (obj_type)) {
2995 : : GstBuffer *buf;
2996 : :
2997 : : /* For buffer lists do not set last buffer. Creating buffer
2998 : : * with meaningful data can be done only with memcpy which will
2999 : : * significantly affect performance */
3000 : 686011 : buf = GST_BUFFER_CAST (obj);
3001 : 686011 : gst_base_sink_set_last_buffer (basesink, buf);
3002 : :
3003 : 686012 : ret = bclass->render (basesink, buf);
3004 : : } else {
3005 : : GstBufferList *buflist;
3006 : :
3007 : 0 : buflist = GST_BUFFER_LIST_CAST (obj);
3008 : :
3009 : 0 : ret = bclass->render_list (basesink, buflist);
3010 : : }
3011 : :
3012 [ - + ]: 686011 : if (do_qos)
3013 : 0 : gst_base_sink_do_render_stats (basesink, FALSE);
3014 : :
3015 [ - + ]: 686011 : if (ret == GST_FLOW_STEP)
3016 : 0 : goto again;
3017 : :
3018 [ - + ]: 686011 : if (G_UNLIKELY (basesink->flushing))
3019 : 0 : goto flushing;
3020 : :
3021 : 686011 : priv->rendered++;
3022 : : }
3023 [ + - ]: 191 : } else if (G_LIKELY (OBJ_IS_EVENT (obj_type))) {
3024 : 191 : GstEvent *event = GST_EVENT_CAST (obj);
3025 : 191 : gboolean event_res = TRUE;
3026 : : GstEventType type;
3027 : :
3028 : 191 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
3029 : :
3030 : 191 : type = GST_EVENT_TYPE (event);
3031 : :
3032 [ - + ]: 191 : GST_DEBUG_OBJECT (basesink, "rendering event %p, type %s", obj,
3033 : : gst_event_type_get_name (type));
3034 : :
3035 [ + - ]: 191 : if (bclass->event)
3036 : 191 : event_res = bclass->event (basesink, event);
3037 : :
3038 : : /* when we get here we could be flushing again when the event handler calls
3039 : : * _wait_eos(). We have to ignore this object in that case. */
3040 [ - + ]: 191 : if (G_UNLIKELY (basesink->flushing))
3041 : 0 : goto flushing;
3042 : :
3043 [ + - ]: 191 : if (G_LIKELY (event_res)) {
3044 : : guint32 seqnum;
3045 : :
3046 : 191 : seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event);
3047 [ - + ]: 191 : GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum);
3048 : :
3049 [ + + - + ]: 191 : switch (type) {
3050 : : case GST_EVENT_EOS:
3051 : : {
3052 : : GstMessage *message;
3053 : :
3054 : : /* the EOS event is completely handled so we mark
3055 : : * ourselves as being in the EOS state. eos is also
3056 : : * protected by the object lock so we can read it when
3057 : : * answering the POSITION query. */
3058 : 36 : GST_OBJECT_LOCK (basesink);
3059 : 36 : basesink->eos = TRUE;
3060 : 36 : GST_OBJECT_UNLOCK (basesink);
3061 : :
3062 : : /* ok, now we can post the message */
3063 [ - + ]: 36 : GST_DEBUG_OBJECT (basesink, "Now posting EOS");
3064 : :
3065 : 36 : message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
3066 : 36 : gst_message_set_seqnum (message, seqnum);
3067 : 36 : gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
3068 : 36 : break;
3069 : : }
3070 : : case GST_EVENT_NEWSEGMENT:
3071 : : /* configure the segment */
3072 : 152 : gst_base_sink_configure_segment (basesink, pad, event,
3073 : : &basesink->segment);
3074 : 152 : break;
3075 : : case GST_EVENT_SINK_MESSAGE:{
3076 : 0 : GstMessage *msg = NULL;
3077 : :
3078 : 0 : gst_event_parse_sink_message (event, &msg);
3079 : :
3080 [ # # ]: 0 : if (msg)
3081 : 0 : gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
3082 : : }
3083 : : default:
3084 : 191 : break;
3085 : : }
3086 : : }
3087 : : } else {
3088 : 0 : g_return_val_if_reached (GST_FLOW_ERROR);
3089 : : }
3090 : :
3091 : : done:
3092 [ - + ]: 686266 : if (step_end) {
3093 : : /* the step ended, check if we need to activate a new step */
3094 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "step ended");
3095 : 0 : stop_stepping (basesink, &basesink->segment, &priv->current_step,
3096 : 0 : priv->current_rstart, priv->current_rstop, basesink->eos);
3097 : 0 : goto again;
3098 : : }
3099 : :
3100 : 686266 : gst_base_sink_perform_qos (basesink, late);
3101 : :
3102 [ - + ]: 686267 : GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj);
3103 : 686267 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3104 : 686267 : return ret;
3105 : :
3106 : : /* ERRORS */
3107 : : sync_failed:
3108 : : {
3109 [ - + ]: 64 : GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
3110 : 64 : goto done;
3111 : : }
3112 : : dropped:
3113 : : {
3114 : 0 : priv->dropped++;
3115 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "buffer late, dropping");
3116 : :
3117 [ # # ]: 0 : if (g_atomic_int_get (&priv->qos_enabled)) {
3118 : : GstMessage *qos_msg;
3119 : : GstClockTime timestamp, duration;
3120 : :
3121 : 0 : timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_obj));
3122 : 0 : duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_obj));
3123 : :
3124 [ # # ][ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
3125 : : "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT
3126 : : ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
3127 : : GST_TIME_ARGS (priv->current_rstart),
3128 : : GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp),
3129 : : GST_TIME_ARGS (duration));
3130 [ # # ]: 0 : GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
3131 : : "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
3132 : : priv->rendered, priv->dropped);
3133 : :
3134 : 0 : qos_msg =
3135 : 0 : gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync,
3136 : : priv->current_rstart, priv->current_sstart, timestamp, duration);
3137 : 0 : gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate,
3138 : : 1000000);
3139 : 0 : gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered,
3140 : : priv->dropped);
3141 : 0 : gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg);
3142 : : }
3143 : 0 : goto done;
3144 : : }
3145 : : flushing:
3146 : : {
3147 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "we are flushing, ignore object");
3148 : 0 : gst_mini_object_unref (obj);
3149 : 686267 : return GST_FLOW_WRONG_STATE;
3150 : : }
3151 : : }
3152 : :
3153 : : /* with STREAM_LOCK, PREROLL_LOCK
3154 : : *
3155 : : * Perform preroll on the given object. For buffers this means
3156 : : * calling the preroll subclass method.
3157 : : * If that succeeds, the state will be commited.
3158 : : *
3159 : : * function does not take ownership of obj.
3160 : : */
3161 : : static GstFlowReturn
3162 : 352 : gst_base_sink_preroll_object (GstBaseSink * basesink, guint8 obj_type,
3163 : : GstMiniObject * obj)
3164 : : {
3165 : : GstFlowReturn ret;
3166 : :
3167 [ - + ]: 352 : GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj);
3168 : :
3169 : : /* if it's a buffer, we need to call the preroll method */
3170 [ + + ][ + + ]: 352 : if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type) && basesink->priv->call_preroll)) {
3171 : : GstBaseSinkClass *bclass;
3172 : : GstBuffer *buf;
3173 : : GstClockTime timestamp;
3174 : :
3175 [ - + ]: 185 : if (OBJ_IS_BUFFERLIST (obj_type)) {
3176 : 0 : buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
3177 [ # # ]: 0 : g_assert (NULL != buf);
3178 : : } else {
3179 : 185 : buf = GST_BUFFER_CAST (obj);
3180 : : }
3181 : :
3182 : 185 : timestamp = GST_BUFFER_TIMESTAMP (buf);
3183 : :
3184 [ - + ][ # # ]: 185 : GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
3185 : : GST_TIME_ARGS (timestamp));
3186 : :
3187 : : /*
3188 : : * For buffer lists do not set last buffer. Creating buffer
3189 : : * with meaningful data can be done only with memcpy which will
3190 : : * significantly affect performance
3191 : : */
3192 [ + - ]: 185 : if (!OBJ_IS_BUFFERLIST (obj_type)) {
3193 : 185 : gst_base_sink_set_last_buffer (basesink, buf);
3194 : : }
3195 : :
3196 : 185 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
3197 [ + + ]: 185 : if (bclass->preroll)
3198 [ - + ]: 184 : if ((ret = bclass->preroll (basesink, buf)) != GST_FLOW_OK)
3199 : 0 : goto preroll_failed;
3200 : :
3201 : 185 : basesink->priv->call_preroll = FALSE;
3202 : : }
3203 : :
3204 : : /* commit state */
3205 [ + + ]: 352 : if (G_LIKELY (basesink->playing_async)) {
3206 [ + + ]: 187 : if (G_UNLIKELY (!gst_base_sink_commit_state (basesink)))
3207 : 4 : goto stopping;
3208 : : }
3209 : :
3210 : 348 : return GST_FLOW_OK;
3211 : :
3212 : : /* ERRORS */
3213 : : preroll_failed:
3214 : : {
3215 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "preroll failed, abort state");
3216 : 0 : gst_element_abort_state (GST_ELEMENT_CAST (basesink));
3217 : 0 : return ret;
3218 : : }
3219 : : stopping:
3220 : : {
3221 [ - + ]: 4 : GST_DEBUG_OBJECT (basesink, "stopping while commiting state");
3222 : 352 : return GST_FLOW_WRONG_STATE;
3223 : : }
3224 : : }
3225 : :
3226 : : /* with STREAM_LOCK, PREROLL_LOCK
3227 : : *
3228 : : * Queue an object for rendering.
3229 : : * The first prerollable object queued will complete the preroll. If the
3230 : : * preroll queue if filled, we render all the objects in the queue.
3231 : : *
3232 : : * This function takes ownership of the object.
3233 : : */
3234 : : static GstFlowReturn
3235 : 686281 : gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,
3236 : : guint8 obj_type, gpointer obj, gboolean prerollable)
3237 : : {
3238 : 686281 : GstFlowReturn ret = GST_FLOW_OK;
3239 : : gint length;
3240 : : GQueue *q;
3241 : :
3242 [ + + ]: 686281 : if (G_UNLIKELY (basesink->need_preroll)) {
3243 [ + + ]: 376 : if (G_LIKELY (prerollable))
3244 : 222 : basesink->preroll_queued++;
3245 : :
3246 : 376 : length = basesink->preroll_queued;
3247 : :
3248 [ - + ]: 376 : GST_DEBUG_OBJECT (basesink, "now %d prerolled items", length);
3249 : :
3250 : : /* first prerollable item needs to finish the preroll */
3251 [ + + ]: 378 : if (length == 1) {
3252 : 179 : ret = gst_base_sink_preroll_object (basesink, obj_type, obj);
3253 [ + + ]: 179 : if (G_UNLIKELY (ret != GST_FLOW_OK))
3254 : 4 : goto preroll_failed;
3255 : : }
3256 : : /* need to recheck if we need preroll, commmit state during preroll
3257 : : * could have made us not need more preroll. */
3258 [ + + ]: 374 : if (G_UNLIKELY (basesink->need_preroll)) {
3259 : : /* see if we can render now, if we can't add the object to the preroll
3260 : : * queue. */
3261 [ + + ]: 368 : if (G_UNLIKELY (length <= basesink->preroll_queue_max_len))
3262 : 204 : goto more_preroll;
3263 : : }
3264 : : }
3265 : : /* we can start rendering (or blocking) the queued object
3266 : : * if any. */
3267 : 686075 : q = basesink->preroll_queue;
3268 [ + + ]: 686267 : while (G_UNLIKELY (!g_queue_is_empty (q))) {
3269 : : GstMiniObject *o;
3270 : : guint8 ot;
3271 : :
3272 : 192 : o = g_queue_pop_head (q);
3273 [ - + ]: 192 : GST_DEBUG_OBJECT (basesink, "rendering queued object %p", o);
3274 : :
3275 : 192 : ot = get_object_type (o);
3276 : :
3277 : : /* do something with the return value */
3278 : 192 : ret = gst_base_sink_render_object (basesink, pad, ot, o);
3279 [ - + ]: 192 : if (ret != GST_FLOW_OK)
3280 : 0 : goto dequeue_failed;
3281 : : }
3282 : :
3283 : : /* now render the object */
3284 : 686073 : ret = gst_base_sink_render_object (basesink, pad, obj_type, obj);
3285 : 686049 : basesink->preroll_queued = 0;
3286 : :
3287 : 686049 : return ret;
3288 : :
3289 : : /* special cases */
3290 : : preroll_failed:
3291 : : {
3292 [ - + ]: 4 : GST_DEBUG_OBJECT (basesink, "preroll failed, reason %s",
3293 : : gst_flow_get_name (ret));
3294 : 4 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3295 : 4 : return ret;
3296 : : }
3297 : : more_preroll:
3298 : : {
3299 : : /* add object to the queue and return */
3300 [ - + ]: 204 : GST_DEBUG_OBJECT (basesink, "need more preroll data %d <= %d",
3301 : : length, basesink->preroll_queue_max_len);
3302 : 204 : g_queue_push_tail (basesink->preroll_queue, obj);
3303 : 204 : return GST_FLOW_OK;
3304 : : }
3305 : : dequeue_failed:
3306 : : {
3307 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "rendering queued objects failed, reason %s",
3308 : : gst_flow_get_name (ret));
3309 : 0 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3310 : 686257 : return ret;
3311 : : }
3312 : : }
3313 : :
3314 : : /* with STREAM_LOCK
3315 : : *
3316 : : * This function grabs the PREROLL_LOCK and adds the object to
3317 : : * the queue.
3318 : : *
3319 : : * This function takes ownership of obj.
3320 : : *
3321 : : * Note: Only GstEvent seem to be passed to this private method
3322 : : */
3323 : : static GstFlowReturn
3324 : 3 : gst_base_sink_queue_object (GstBaseSink * basesink, GstPad * pad,
3325 : : GstMiniObject * obj, gboolean prerollable)
3326 : : {
3327 : : GstFlowReturn ret;
3328 : :
3329 : 3 : GST_PAD_PREROLL_LOCK (pad);
3330 [ - + ]: 3 : if (G_UNLIKELY (basesink->flushing))
3331 : 0 : goto flushing;
3332 : :
3333 [ - + ]: 3 : if (G_UNLIKELY (basesink->priv->received_eos))
3334 : 0 : goto was_eos;
3335 : :
3336 : 3 : ret =
3337 : : gst_base_sink_queue_object_unlocked (basesink, pad, _PR_IS_EVENT, obj,
3338 : : prerollable);
3339 : 3 : GST_PAD_PREROLL_UNLOCK (pad);
3340 : :
3341 : 3 : return ret;
3342 : :
3343 : : /* ERRORS */
3344 : : flushing:
3345 : : {
3346 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "sink is flushing");
3347 : 0 : GST_PAD_PREROLL_UNLOCK (pad);
3348 : 0 : gst_mini_object_unref (obj);
3349 : 0 : return GST_FLOW_WRONG_STATE;
3350 : : }
3351 : : was_eos:
3352 : : {
3353 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink,
3354 : : "we are EOS, dropping object, return UNEXPECTED");
3355 : 0 : GST_PAD_PREROLL_UNLOCK (pad);
3356 : 0 : gst_mini_object_unref (obj);
3357 : 3 : return GST_FLOW_UNEXPECTED;
3358 : : }
3359 : : }
3360 : :
3361 : : static void
3362 : 5 : gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
3363 : : {
3364 : : /* make sure we are not blocked on the clock also clear any pending
3365 : : * eos state. */
3366 : 5 : gst_base_sink_set_flushing (basesink, pad, TRUE);
3367 : :
3368 : : /* we grab the stream lock but that is not needed since setting the
3369 : : * sink to flushing would make sure no state commit is being done
3370 : : * anymore */
3371 : 5 : GST_PAD_STREAM_LOCK (pad);
3372 : 5 : gst_base_sink_reset_qos (basesink);
3373 : : /* and we need to commit our state again on the next
3374 : : * prerolled buffer */
3375 : 5 : basesink->playing_async = TRUE;
3376 [ + + ]: 5 : if (basesink->priv->async_enabled) {
3377 : 3 : gst_element_lost_state (GST_ELEMENT_CAST (basesink));
3378 : : } else {
3379 : 2 : basesink->priv->have_latency = TRUE;
3380 : : }
3381 : 5 : gst_base_sink_set_last_buffer (basesink, NULL);
3382 : 5 : GST_PAD_STREAM_UNLOCK (pad);
3383 : 5 : }
3384 : :
3385 : : static void
3386 : 5 : gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad)
3387 : : {
3388 : : /* unset flushing so we can accept new data, this also flushes out any EOS
3389 : : * event. */
3390 : 5 : gst_base_sink_set_flushing (basesink, pad, FALSE);
3391 : :
3392 : : /* for position reporting */
3393 : 5 : GST_OBJECT_LOCK (basesink);
3394 : 5 : basesink->priv->current_sstart = GST_CLOCK_TIME_NONE;
3395 : 5 : basesink->priv->current_sstop = GST_CLOCK_TIME_NONE;
3396 : 5 : basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE;
3397 : 5 : basesink->priv->call_preroll = TRUE;
3398 : 5 : basesink->priv->current_step.valid = FALSE;
3399 : 5 : basesink->priv->pending_step.valid = FALSE;
3400 [ + - ]: 5 : if (basesink->pad_mode == GST_ACTIVATE_PUSH) {
3401 : : /* we need new segment info after the flush. */
3402 : 5 : basesink->have_newsegment = FALSE;
3403 : 5 : gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
3404 : 5 : gst_segment_init (basesink->abidata.ABI.clip_segment, GST_FORMAT_UNDEFINED);
3405 : : }
3406 : 5 : GST_OBJECT_UNLOCK (basesink);
3407 : 5 : }
3408 : :
3409 : : static gboolean
3410 : 147540 : gst_base_sink_event (GstPad * pad, GstEvent * event)
3411 : : {
3412 : : GstBaseSink *basesink;
3413 : 147540 : gboolean result = TRUE;
3414 : : GstBaseSinkClass *bclass;
3415 : :
3416 : 147540 : basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
3417 : :
3418 : 147539 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
3419 : :
3420 [ - + ]: 147539 : GST_DEBUG_OBJECT (basesink, "received event %p %" GST_PTR_FORMAT, event,
3421 : : event);
3422 : :
3423 [ + + + + : 147531 : switch (GST_EVENT_TYPE (event)) {
+ ]
3424 : : case GST_EVENT_EOS:
3425 : : {
3426 : : GstFlowReturn ret;
3427 : :
3428 : 39 : GST_PAD_PREROLL_LOCK (pad);
3429 [ - + ]: 39 : if (G_UNLIKELY (basesink->flushing))
3430 : 0 : goto flushing;
3431 : :
3432 [ + + ]: 39 : if (G_UNLIKELY (basesink->priv->received_eos)) {
3433 : : /* we can't accept anything when we are EOS */
3434 : 1 : result = FALSE;
3435 : 1 : gst_event_unref (event);
3436 : : } else {
3437 : : /* we set the received EOS flag here so that we can use it when testing if
3438 : : * we are prerolled and to refuse more buffers. */
3439 : 38 : basesink->priv->received_eos = TRUE;
3440 : :
3441 : : /* EOS is a prerollable object, we call the unlocked version because it
3442 : : * does not check the received_eos flag. */
3443 : 38 : ret = gst_base_sink_queue_object_unlocked (basesink, pad,
3444 : : _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), TRUE);
3445 [ + + ]: 38 : if (G_UNLIKELY (ret != GST_FLOW_OK))
3446 : 1 : result = FALSE;
3447 : : }
3448 : 39 : GST_PAD_PREROLL_UNLOCK (pad);
3449 : 39 : break;
3450 : : }
3451 : : case GST_EVENT_NEWSEGMENT:
3452 : : {
3453 : : GstFlowReturn ret;
3454 : : gboolean update;
3455 : :
3456 [ - + ]: 157 : GST_DEBUG_OBJECT (basesink, "newsegment %p", event);
3457 : :
3458 : 157 : GST_PAD_PREROLL_LOCK (pad);
3459 [ - + ]: 157 : if (G_UNLIKELY (basesink->flushing))
3460 : 0 : goto flushing;
3461 : :
3462 : 157 : gst_event_parse_new_segment_full (event, &update, NULL, NULL, NULL, NULL,
3463 : : NULL, NULL);
3464 : :
3465 [ + + ][ + - ]: 157 : if (G_UNLIKELY (basesink->priv->received_eos && !update)) {
3466 : : /* we can't accept anything when we are EOS */
3467 : 1 : result = FALSE;
3468 : 1 : gst_event_unref (event);
3469 : : } else {
3470 : : /* the new segment is a non prerollable item and does not block anything,
3471 : : * we need to configure the current clipping segment and insert the event
3472 : : * in the queue to serialize it with the buffers for rendering. */
3473 : 156 : gst_base_sink_configure_segment (basesink, pad, event,
3474 : : basesink->abidata.ABI.clip_segment);
3475 : :
3476 : 156 : ret =
3477 : : gst_base_sink_queue_object_unlocked (basesink, pad,
3478 : : _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), FALSE);
3479 [ - + ]: 156 : if (G_UNLIKELY (ret != GST_FLOW_OK))
3480 : 0 : result = FALSE;
3481 : : else {
3482 : 156 : GST_OBJECT_LOCK (basesink);
3483 : 156 : basesink->have_newsegment = TRUE;
3484 : 156 : GST_OBJECT_UNLOCK (basesink);
3485 : : }
3486 : : }
3487 : 157 : GST_PAD_PREROLL_UNLOCK (pad);
3488 : 157 : break;
3489 : : }
3490 : : case GST_EVENT_FLUSH_START:
3491 [ + - ]: 5 : if (bclass->event)
3492 : 5 : bclass->event (basesink, event);
3493 : :
3494 [ - + ]: 5 : GST_DEBUG_OBJECT (basesink, "flush-start %p", event);
3495 : :
3496 : 5 : gst_base_sink_flush_start (basesink, pad);
3497 : :
3498 : 5 : gst_event_unref (event);
3499 : 5 : break;
3500 : : case GST_EVENT_FLUSH_STOP:
3501 [ + - ]: 5 : if (bclass->event)
3502 : 5 : bclass->event (basesink, event);
3503 : :
3504 [ - + ]: 5 : GST_DEBUG_OBJECT (basesink, "flush-stop %p", event);
3505 : :
3506 : 5 : gst_base_sink_flush_stop (basesink, pad);
3507 : :
3508 : 5 : gst_event_unref (event);
3509 : 5 : break;
3510 : : default:
3511 : : /* other events are sent to queue or subclass depending on if they
3512 : : * are serialized. */
3513 [ + + ]: 147325 : if (GST_EVENT_IS_SERIALIZED (event)) {
3514 : 3 : gst_base_sink_queue_object (basesink, pad,
3515 : : GST_MINI_OBJECT_CAST (event), FALSE);
3516 : : } else {
3517 [ + ]: 147322 : if (bclass->event)
3518 : 147327 : bclass->event (basesink, event);
3519 : 147326 : gst_event_unref (event);
3520 : : }
3521 : 147334 : break;
3522 : : }
3523 : : done:
3524 : 147540 : gst_object_unref (basesink);
3525 : :
3526 : 147540 : return result;
3527 : :
3528 : : /* ERRORS */
3529 : : flushing:
3530 : : {
3531 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "we are flushing");
3532 : 0 : GST_PAD_PREROLL_UNLOCK (pad);
3533 : 0 : result = FALSE;
3534 : 0 : gst_event_unref (event);
3535 : 0 : goto done;
3536 : : }
3537 : : }
3538 : :
3539 : : /* default implementation to calculate the start and end
3540 : : * timestamps on a buffer, subclasses can override
3541 : : */
3542 : : static void
3543 : 1372070 : gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
3544 : : GstClockTime * start, GstClockTime * end)
3545 : : {
3546 : : GstClockTime timestamp, duration;
3547 : :
3548 : 1372070 : timestamp = GST_BUFFER_TIMESTAMP (buffer);
3549 [ + ]: 1372070 : if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
3550 : :
3551 : : /* get duration to calculate end time */
3552 : 1372135 : duration = GST_BUFFER_DURATION (buffer);
3553 [ + + ]: 1372135 : if (GST_CLOCK_TIME_IS_VALID (duration)) {
3554 : 10467 : *end = timestamp + duration;
3555 : : }
3556 : 1372135 : *start = timestamp;
3557 : : }
3558 : 1372070 : }
3559 : :
3560 : : /* must be called with PREROLL_LOCK */
3561 : : static gboolean
3562 : 228 : gst_base_sink_needs_preroll (GstBaseSink * basesink)
3563 : : {
3564 : : gboolean is_prerolled, res;
3565 : :
3566 : : /* we have 2 cases where the PREROLL_LOCK is released:
3567 : : * 1) we are blocking in the PREROLL_LOCK and thus are prerolled.
3568 : : * 2) we are syncing on the clock
3569 : : */
3570 [ + + ][ + + ]: 228 : is_prerolled = basesink->have_preroll || basesink->priv->received_eos;
3571 : 228 : res = !is_prerolled;
3572 : :
3573 [ - + ]: 228 : GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d => needs preroll: %d",
3574 : : basesink->have_preroll, basesink->priv->received_eos, res);
3575 : :
3576 : 228 : return res;
3577 : : }
3578 : :
3579 : : /* with STREAM_LOCK, PREROLL_LOCK
3580 : : *
3581 : : * Takes a buffer and compare the timestamps with the last segment.
3582 : : * If the buffer falls outside of the segment boundaries, drop it.
3583 : : * Else queue the buffer for preroll and rendering.
3584 : : *
3585 : : * This function takes ownership of the buffer.
3586 : : */
3587 : : static GstFlowReturn
3588 : 686090 : gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
3589 : : guint8 obj_type, gpointer obj)
3590 : : {
3591 : : GstBaseSinkClass *bclass;
3592 : : GstFlowReturn result;
3593 : 686090 : GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
3594 : : GstSegment *clip_segment;
3595 : : GstBuffer *time_buf;
3596 : :
3597 [ - + ]: 686090 : if (G_UNLIKELY (basesink->flushing))
3598 : 0 : goto flushing;
3599 : :
3600 [ + + ]: 686090 : if (G_UNLIKELY (basesink->priv->received_eos))
3601 : 1 : goto was_eos;
3602 : :
3603 [ - + ]: 686089 : if (OBJ_IS_BUFFERLIST (obj_type)) {
3604 : 0 : time_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
3605 [ # # ]: 0 : g_assert (NULL != time_buf);
3606 : : } else {
3607 : 686089 : time_buf = GST_BUFFER_CAST (obj);
3608 : : }
3609 : :
3610 : : /* for code clarity */
3611 : 686089 : clip_segment = basesink->abidata.ABI.clip_segment;
3612 : :
3613 [ + + ]: 686089 : if (G_UNLIKELY (!basesink->have_newsegment)) {
3614 : : gboolean sync;
3615 : :
3616 : 1 : sync = gst_base_sink_get_sync (basesink);
3617 [ + - ]: 1 : if (sync) {
3618 [ + - ][ - + ]: 1 : GST_ELEMENT_WARNING (basesink, STREAM, FAILED,
[ + - ][ - + ]
3619 : : (_("Internal data flow problem.")),
3620 : : ("Received buffer without a new-segment. Assuming timestamps start from 0."));
3621 : : }
3622 : :
3623 : : /* this means this sink will assume timestamps start from 0 */
3624 : 1 : GST_OBJECT_LOCK (basesink);
3625 : 1 : clip_segment->start = 0;
3626 : 1 : clip_segment->stop = -1;
3627 : 1 : basesink->segment.start = 0;
3628 : 1 : basesink->segment.stop = -1;
3629 : 1 : basesink->have_newsegment = TRUE;
3630 : 1 : GST_OBJECT_UNLOCK (basesink);
3631 : : }
3632 : :
3633 : 686088 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
3634 : :
3635 : : /* check if the buffer needs to be dropped, we first ask the subclass for the
3636 : : * start and end */
3637 [ + - ]: 686088 : if (bclass->get_times)
3638 : 686088 : bclass->get_times (basesink, time_buf, &start, &end);
3639 : :
3640 [ + + ]: 686089 : if (!GST_CLOCK_TIME_IS_VALID (start)) {
3641 : : /* if the subclass does not want sync, we use our own values so that we at
3642 : : * least clip the buffer to the segment */
3643 : 6 : gst_base_sink_get_times (basesink, time_buf, &start, &end);
3644 : : }
3645 : :
3646 [ - + ][ # # ]: 686089 : GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
3647 : : ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
3648 : :
3649 : : /* a dropped buffer does not participate in anything */
3650 [ + + ][ + + ]: 686089 : if (GST_CLOCK_TIME_IS_VALID (start) &&
3651 : 686083 : (clip_segment->format == GST_FORMAT_TIME)) {
3652 [ + + ]: 17 : if (G_UNLIKELY (!gst_segment_clip (clip_segment,
3653 : : GST_FORMAT_TIME, (gint64) start, (gint64) end, NULL, NULL)))
3654 : 3 : goto out_of_segment;
3655 : : }
3656 : :
3657 : : /* now we can process the buffer in the queue, this function takes ownership
3658 : : * of the buffer */
3659 : 686086 : result = gst_base_sink_queue_object_unlocked (basesink, pad,
3660 : : obj_type, obj, TRUE);
3661 : 686085 : return result;
3662 : :
3663 : : /* ERRORS */
3664 : : flushing:
3665 : : {
3666 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "sink is flushing");
3667 : 0 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3668 : 0 : return GST_FLOW_WRONG_STATE;
3669 : : }
3670 : : was_eos:
3671 : : {
3672 [ - + ]: 1 : GST_DEBUG_OBJECT (basesink,
3673 : : "we are EOS, dropping object, return UNEXPECTED");
3674 : 1 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3675 : 1 : return GST_FLOW_UNEXPECTED;
3676 : : }
3677 : : out_of_segment:
3678 : : {
3679 [ - + ]: 3 : GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment");
3680 : 3 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3681 : 686089 : return GST_FLOW_OK;
3682 : : }
3683 : : }
3684 : :
3685 : : /* with STREAM_LOCK
3686 : : */
3687 : : static GstFlowReturn
3688 : 647474 : gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,
3689 : : guint8 obj_type, gpointer obj)
3690 : : {
3691 : : GstFlowReturn result;
3692 : :
3693 [ - + ]: 647474 : if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PUSH))
3694 : 0 : goto wrong_mode;
3695 : :
3696 : 647474 : GST_PAD_PREROLL_LOCK (pad);
3697 : 647475 : result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);
3698 : 647474 : GST_PAD_PREROLL_UNLOCK (pad);
3699 : :
3700 : : done:
3701 : 647475 : return result;
3702 : :
3703 : : /* ERRORS */
3704 : : wrong_mode:
3705 : : {
3706 : 0 : GST_OBJECT_LOCK (pad);
3707 [ # # ][ # # ]: 0 : GST_WARNING_OBJECT (basesink,
[ # # ][ # # ]
[ # # ][ # # ]
3708 : : "Push on pad %s:%s, but it was not activated in push mode",
3709 : : GST_DEBUG_PAD_NAME (pad));
3710 : 0 : GST_OBJECT_UNLOCK (pad);
3711 : 0 : gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
3712 : : /* we don't post an error message this will signal to the peer
3713 : : * pushing that EOS is reached. */
3714 : 0 : result = GST_FLOW_UNEXPECTED;
3715 : 0 : goto done;
3716 : : }
3717 : : }
3718 : :
3719 : : static GstFlowReturn
3720 : 647469 : gst_base_sink_chain (GstPad * pad, GstBuffer * buf)
3721 : : {
3722 : : GstBaseSink *basesink;
3723 : :
3724 : 647469 : basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
3725 : :
3726 : 647474 : return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);
3727 : : }
3728 : :
3729 : : static GstFlowReturn
3730 : 0 : gst_base_sink_chain_list (GstPad * pad, GstBufferList * list)
3731 : : {
3732 : : GstBaseSink *basesink;
3733 : : GstBaseSinkClass *bclass;
3734 : : GstFlowReturn result;
3735 : :
3736 : 0 : basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
3737 : 0 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
3738 : :
3739 [ # # ]: 0 : if (G_LIKELY (bclass->render_list)) {
3740 : 0 : result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFERLIST, list);
3741 : : } else {
3742 : : GstBufferListIterator *it;
3743 : : GstBuffer *group;
3744 : :
3745 [ # # ]: 0 : GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
3746 : :
3747 : 0 : it = gst_buffer_list_iterate (list);
3748 : :
3749 [ # # ]: 0 : if (gst_buffer_list_iterator_next_group (it)) {
3750 : : do {
3751 : 0 : group = gst_buffer_list_iterator_merge_group (it);
3752 [ # # ]: 0 : if (group == NULL) {
3753 : 0 : group = gst_buffer_new ();
3754 [ # # ]: 0 : GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
3755 : : } else {
3756 [ # # ]: 0 : GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining group");
3757 : : }
3758 : 0 : result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, group);
3759 : : } while (result == GST_FLOW_OK
3760 [ # # ][ # # ]: 0 : && gst_buffer_list_iterator_next_group (it));
3761 : : } else {
3762 [ # # ]: 0 : GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
3763 : 0 : result =
3764 : : gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER,
3765 : 0 : gst_buffer_new ());
3766 : : }
3767 : 0 : gst_buffer_list_iterator_free (it);
3768 : 0 : gst_buffer_list_unref (list);
3769 : : }
3770 : 0 : return result;
3771 : : }
3772 : :
3773 : :
3774 : : static gboolean
3775 : 0 : gst_base_sink_default_do_seek (GstBaseSink * sink, GstSegment * segment)
3776 : : {
3777 : 0 : gboolean res = TRUE;
3778 : :
3779 : : /* update our offset if the start/stop position was updated */
3780 [ # # ]: 0 : if (segment->format == GST_FORMAT_BYTES) {
3781 : 0 : segment->time = segment->start;
3782 [ # # ]: 0 : } else if (segment->start == 0) {
3783 : : /* seek to start, we can implement a default for this. */
3784 : 0 : segment->time = 0;
3785 : : } else {
3786 : 0 : res = FALSE;
3787 [ # # ]: 0 : GST_INFO_OBJECT (sink, "Can't do a default seek");
3788 : : }
3789 : :
3790 : 0 : return res;
3791 : : }
3792 : :
3793 : : #define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))
3794 : :
3795 : : static gboolean
3796 : 0 : gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
3797 : : GstEvent * event, GstSegment * segment)
3798 : : {
3799 : : /* By default, we try one of 2 things:
3800 : : * - For absolute seek positions, convert the requested position to our
3801 : : * configured processing format and place it in the output segment \
3802 : : * - For relative seek positions, convert our current (input) values to the
3803 : : * seek format, adjust by the relative seek offset and then convert back to
3804 : : * the processing format
3805 : : */
3806 : : GstSeekType cur_type, stop_type;
3807 : : gint64 cur, stop;
3808 : : GstSeekFlags flags;
3809 : : GstFormat seek_format, dest_format;
3810 : : gdouble rate;
3811 : : gboolean update;
3812 : 0 : gboolean res = TRUE;
3813 : :
3814 : 0 : gst_event_parse_seek (event, &rate, &seek_format, &flags,
3815 : : &cur_type, &cur, &stop_type, &stop);
3816 : 0 : dest_format = segment->format;
3817 : :
3818 [ # # ]: 0 : if (seek_format == dest_format) {
3819 : 0 : gst_segment_set_seek (segment, rate, seek_format, flags,
3820 : : cur_type, cur, stop_type, stop, &update);
3821 : 0 : return TRUE;
3822 : : }
3823 : :
3824 [ # # ]: 0 : if (cur_type != GST_SEEK_TYPE_NONE) {
3825 : : /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
3826 : 0 : res =
3827 : 0 : gst_pad_query_convert (sink->sinkpad, seek_format, cur, &dest_format,
3828 : : &cur);
3829 : 0 : cur_type = GST_SEEK_TYPE_SET;
3830 : : }
3831 : :
3832 [ # # ][ # # ]: 0 : if (res && stop_type != GST_SEEK_TYPE_NONE) {
3833 : : /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
3834 : 0 : res =
3835 : 0 : gst_pad_query_convert (sink->sinkpad, seek_format, stop, &dest_format,
3836 : : &stop);
3837 : 0 : stop_type = GST_SEEK_TYPE_SET;
3838 : : }
3839 : :
3840 : : /* And finally, configure our output segment in the desired format */
3841 : 0 : gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur,
3842 : : stop_type, stop, &update);
3843 : :
3844 [ # # ]: 0 : if (!res)
3845 : 0 : goto no_format;
3846 : :
3847 : 0 : return res;
3848 : :
3849 : : no_format:
3850 : : {
3851 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted.");
3852 : 0 : return FALSE;
3853 : : }
3854 : : }
3855 : :
3856 : : /* perform a seek, only executed in pull mode */
3857 : : static gboolean
3858 : 0 : gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
3859 : : {
3860 : : gboolean flush;
3861 : : gdouble rate;
3862 : : GstFormat seek_format, dest_format;
3863 : : GstSeekFlags flags;
3864 : : GstSeekType cur_type, stop_type;
3865 : 0 : gboolean seekseg_configured = FALSE;
3866 : : gint64 cur, stop;
3867 : 0 : gboolean update, res = TRUE;
3868 : : GstSegment seeksegment;
3869 : :
3870 : 0 : dest_format = sink->segment.format;
3871 : :
3872 [ # # ]: 0 : if (event) {
3873 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "performing seek with event %p", event);
3874 : 0 : gst_event_parse_seek (event, &rate, &seek_format, &flags,
3875 : : &cur_type, &cur, &stop_type, &stop);
3876 : :
3877 : 0 : flush = flags & GST_SEEK_FLAG_FLUSH;
3878 : : } else {
3879 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "performing seek without event");
3880 : 0 : flush = FALSE;
3881 : : }
3882 : :
3883 [ # # ]: 0 : if (flush) {
3884 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "flushing upstream");
3885 : 0 : gst_pad_push_event (pad, gst_event_new_flush_start ());
3886 : 0 : gst_base_sink_flush_start (sink, pad);
3887 : : } else {
3888 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "pausing pulling thread");
3889 : : }
3890 : :
3891 : 0 : GST_PAD_STREAM_LOCK (pad);
3892 : :
3893 : : /* If we configured the seeksegment above, don't overwrite it now. Otherwise
3894 : : * copy the current segment info into the temp segment that we can actually
3895 : : * attempt the seek with. We only update the real segment if the seek suceeds. */
3896 [ # # ]: 0 : if (!seekseg_configured) {
3897 : 0 : memcpy (&seeksegment, &sink->segment, sizeof (GstSegment));
3898 : :
3899 : : /* now configure the final seek segment */
3900 [ # # ]: 0 : if (event) {
3901 [ # # ]: 0 : if (sink->segment.format != seek_format) {
3902 : : /* OK, here's where we give the subclass a chance to convert the relative
3903 : : * seek into an absolute one in the processing format. We set up any
3904 : : * absolute seek above, before taking the stream lock. */
3905 [ # # ]: 0 : if (!gst_base_sink_default_prepare_seek_segment (sink, event,
3906 : : &seeksegment)) {
3907 [ # # ]: 0 : GST_DEBUG_OBJECT (sink,
3908 : : "Preparing the seek failed after flushing. " "Aborting seek");
3909 : 0 : res = FALSE;
3910 : : }
3911 : : } else {
3912 : : /* The seek format matches our processing format, no need to ask the
3913 : : * the subclass to configure the segment. */
3914 : 0 : gst_segment_set_seek (&seeksegment, rate, seek_format, flags,
3915 : : cur_type, cur, stop_type, stop, &update);
3916 : : }
3917 : : }
3918 : : /* Else, no seek event passed, so we're just (re)starting the
3919 : : current segment. */
3920 : : }
3921 : :
3922 [ # # ]: 0 : if (res) {
3923 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "segment configured from %" G_GINT64_FORMAT
3924 : : " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
3925 : : seeksegment.start, seeksegment.stop, seeksegment.last_stop);
3926 : :
3927 : : /* do the seek, segment.last_stop contains the new position. */
3928 : 0 : res = gst_base_sink_default_do_seek (sink, &seeksegment);
3929 : : }
3930 : :
3931 : :
3932 [ # # ]: 0 : if (flush) {
3933 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "stop flushing upstream");
3934 : 0 : gst_pad_push_event (pad, gst_event_new_flush_stop ());
3935 : 0 : gst_base_sink_flush_stop (sink, pad);
3936 [ # # ][ # # ]: 0 : } else if (res && sink->abidata.ABI.running) {
3937 : : /* we are running the current segment and doing a non-flushing seek,
3938 : : * close the segment first based on the last_stop. */
3939 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT
3940 : : " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.last_stop);
3941 : : }
3942 : :
3943 : : /* The subclass must have converted the segment to the processing format
3944 : : * by now */
3945 [ # # ][ # # ]: 0 : if (res && seeksegment.format != dest_format) {
3946 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "Subclass failed to prepare a seek segment "
3947 : : "in the correct format. Aborting seek.");
3948 : 0 : res = FALSE;
3949 : : }
3950 : :
3951 : : /* if successfull seek, we update our real segment and push
3952 : : * out the new segment. */
3953 [ # # ]: 0 : if (res) {
3954 : 0 : memcpy (&sink->segment, &seeksegment, sizeof (GstSegment));
3955 : :
3956 [ # # ]: 0 : if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3957 : 0 : gst_element_post_message (GST_ELEMENT (sink),
3958 : 0 : gst_message_new_segment_start (GST_OBJECT (sink),
3959 : : sink->segment.format, sink->segment.last_stop));
3960 : : }
3961 : : }
3962 : :
3963 : 0 : sink->priv->discont = TRUE;
3964 : 0 : sink->abidata.ABI.running = TRUE;
3965 : :
3966 : 0 : GST_PAD_STREAM_UNLOCK (pad);
3967 : :
3968 : 0 : return res;
3969 : : }
3970 : :
3971 : : static void
3972 : 0 : set_step_info (GstBaseSink * sink, GstStepInfo * current, GstStepInfo * pending,
3973 : : guint seqnum, GstFormat format, guint64 amount, gdouble rate,
3974 : : gboolean flush, gboolean intermediate)
3975 : : {
3976 : 0 : GST_OBJECT_LOCK (sink);
3977 : 0 : pending->seqnum = seqnum;
3978 : 0 : pending->format = format;
3979 : 0 : pending->amount = amount;
3980 : 0 : pending->position = 0;
3981 : 0 : pending->rate = rate;
3982 : 0 : pending->flush = flush;
3983 : 0 : pending->intermediate = intermediate;
3984 : 0 : pending->valid = TRUE;
3985 : : /* flush invalidates the current stepping segment */
3986 [ # # ]: 0 : if (flush)
3987 : 0 : current->valid = FALSE;
3988 : 0 : GST_OBJECT_UNLOCK (sink);
3989 : 0 : }
3990 : :
3991 : : static gboolean
3992 : 0 : gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
3993 : : {
3994 : : GstBaseSinkPrivate *priv;
3995 : : GstBaseSinkClass *bclass;
3996 : : gboolean flush, intermediate;
3997 : : gdouble rate;
3998 : : GstFormat format;
3999 : : guint64 amount;
4000 : : guint seqnum;
4001 : : GstStepInfo *pending, *current;
4002 : : GstMessage *message;
4003 : :
4004 : 0 : bclass = GST_BASE_SINK_GET_CLASS (sink);
4005 : 0 : priv = sink->priv;
4006 : :
4007 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "performing step with event %p", event);
4008 : :
4009 : 0 : gst_event_parse_step (event, &format, &amount, &rate, &flush, &intermediate);
4010 : 0 : seqnum = gst_event_get_seqnum (event);
4011 : :
4012 : 0 : pending = &priv->pending_step;
4013 : 0 : current = &priv->current_step;
4014 : :
4015 : : /* post message first */
4016 : 0 : message = gst_message_new_step_start (GST_OBJECT (sink), FALSE, format,
4017 : : amount, rate, flush, intermediate);
4018 : 0 : gst_message_set_seqnum (message, seqnum);
4019 : 0 : gst_element_post_message (GST_ELEMENT (sink), message);
4020 : :
4021 [ # # ]: 0 : if (flush) {
4022 : : /* we need to call ::unlock before locking PREROLL_LOCK
4023 : : * since we lock it before going into ::render */
4024 [ # # ]: 0 : if (bclass->unlock)
4025 : 0 : bclass->unlock (sink);
4026 : :
4027 : 0 : GST_PAD_PREROLL_LOCK (sink->sinkpad);
4028 : : /* now that we have the PREROLL lock, clear our unlock request */
4029 [ # # ]: 0 : if (bclass->unlock_stop)
4030 : 0 : bclass->unlock_stop (sink);
4031 : :
4032 : : /* update the stepinfo and make it valid */
4033 : 0 : set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
4034 : : intermediate);
4035 : :
4036 [ # # ]: 0 : if (sink->priv->async_enabled) {
4037 : : /* and we need to commit our state again on the next
4038 : : * prerolled buffer */
4039 : 0 : sink->playing_async = TRUE;
4040 : 0 : priv->pending_step.need_preroll = TRUE;
4041 : 0 : sink->need_preroll = FALSE;
4042 : 0 : gst_element_lost_state_full (GST_ELEMENT_CAST (sink), FALSE);
4043 : : } else {
4044 : 0 : sink->priv->have_latency = TRUE;
4045 : 0 : sink->need_preroll = FALSE;
4046 : : }
4047 : 0 : priv->current_sstart = GST_CLOCK_TIME_NONE;
4048 : 0 : priv->current_sstop = GST_CLOCK_TIME_NONE;
4049 : 0 : priv->eos_rtime = GST_CLOCK_TIME_NONE;
4050 : 0 : priv->call_preroll = TRUE;
4051 : 0 : gst_base_sink_set_last_buffer (sink, NULL);
4052 : 0 : gst_base_sink_reset_qos (sink);
4053 : :
4054 [ # # ]: 0 : if (sink->clock_id) {
4055 : 0 : gst_clock_id_unschedule (sink->clock_id);
4056 : : }
4057 : :
4058 [ # # ]: 0 : if (sink->have_preroll) {
4059 [ # # ]: 0 : GST_DEBUG_OBJECT (sink, "signal waiter");
4060 : 0 : priv->step_unlock = TRUE;
4061 : 0 : GST_PAD_PREROLL_SIGNAL (sink->sinkpad);
4062 : : }
4063 : 0 : GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
4064 : : } else {
4065 : : /* update the stepinfo and make it valid */
4066 : 0 : set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
4067 : : intermediate);
4068 : : }
4069 : :
4070 : 0 : return TRUE;
4071 : : }
4072 : :
4073 : : /* with STREAM_LOCK
4074 : : */
4075 : : static void
4076 : 38619 : gst_base_sink_loop (GstPad * pad)
4077 : : {
4078 : : GstBaseSink *basesink;
4079 : 38619 : GstBuffer *buf = NULL;
4080 : : GstFlowReturn result;
4081 : : guint blocksize;
4082 : : guint64 offset;
4083 : :
4084 : 38619 : basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
4085 : :
4086 [ - + ]: 38619 : g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
4087 : :
4088 [ - + ]: 38619 : if ((blocksize = basesink->priv->blocksize) == 0)
4089 : 0 : blocksize = -1;
4090 : :
4091 : 38619 : offset = basesink->segment.last_stop;
4092 : :
4093 [ - + ]: 38619 : GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u",
4094 : : offset, blocksize);
4095 : :
4096 : 38619 : result = gst_pad_pull_range (pad, offset, blocksize, &buf);
4097 [ + + ]: 38619 : if (G_UNLIKELY (result != GST_FLOW_OK))
4098 : 4 : goto paused;
4099 : :
4100 [ - + ]: 38615 : if (G_UNLIKELY (buf == NULL))
4101 : 0 : goto no_buffer;
4102 : :
4103 : 38615 : offset += GST_BUFFER_SIZE (buf);
4104 : :
4105 : 38615 : gst_segment_set_last_stop (&basesink->segment, GST_FORMAT_BYTES, offset);
4106 : :
4107 : 38615 : GST_PAD_PREROLL_LOCK (pad);
4108 : 38615 : result = gst_base_sink_chain_unlocked (basesink, pad, _PR_IS_BUFFER, buf);
4109 : 38615 : GST_PAD_PREROLL_UNLOCK (pad);
4110 [ + + ]: 38615 : if (G_UNLIKELY (result != GST_FLOW_OK))
4111 : 2 : goto paused;
4112 : :
4113 : 38613 : return;
4114 : :
4115 : : /* ERRORS */
4116 : : paused:
4117 : : {
4118 [ - + ]: 6 : GST_LOG_OBJECT (basesink, "pausing task, reason %s",
4119 : : gst_flow_get_name (result));
4120 : 6 : gst_pad_pause_task (pad);
4121 [ + + ]: 6 : if (result == GST_FLOW_UNEXPECTED) {
4122 : : /* perform EOS logic */
4123 [ - + ]: 3 : if (basesink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4124 : 0 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
4125 : : gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
4126 : : basesink->segment.format, basesink->segment.last_stop));
4127 : : } else {
4128 : 3 : gst_base_sink_event (pad, gst_event_new_eos ());
4129 : : }
4130 [ + - ][ - + ]: 3 : } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_UNEXPECTED) {
4131 : : /* for fatal errors we post an error message, post the error
4132 : : * first so the app knows about the error first.
4133 : : * wrong-state is not a fatal error because it happens due to
4134 : : * flushing and posting an error message in that case is the
4135 : : * wrong thing to do, e.g. when basesrc is doing a flushing
4136 : : * seek. */
4137 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
[ # # ][ # # ]
4138 : : (_("Internal data stream error.")),
4139 : : ("stream stopped, reason %s", gst_flow_get_name (result)));
4140 : 0 : gst_base_sink_event (pad, gst_event_new_eos ());
4141 : : }
4142 : 6 : return;
4143 : : }
4144 : : no_buffer:
4145 : : {
4146 [ # # ]: 0 : GST_LOG_OBJECT (basesink, "no buffer, pausing");
4147 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
[ # # ][ # # ]
4148 : : (_("Internal data flow error.")), ("element returned NULL buffer"));
4149 : 0 : result = GST_FLOW_ERROR;
4150 : 38619 : goto paused;
4151 : : }
4152 : : }
4153 : :
4154 : : static gboolean
4155 : 858 : gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
4156 : : gboolean flushing)
4157 : : {
4158 : : GstBaseSinkClass *bclass;
4159 : :
4160 : 858 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
4161 : :
4162 [ + + ]: 858 : if (flushing) {
4163 : : /* unlock any subclasses, we need to do this before grabbing the
4164 : : * PREROLL_LOCK since we hold this lock before going into ::render. */
4165 [ + + ]: 429 : if (bclass->unlock)
4166 : 7 : bclass->unlock (basesink);
4167 : : }
4168 : :
4169 : 858 : GST_PAD_PREROLL_LOCK (pad);
4170 : 858 : basesink->flushing = flushing;
4171 [ + + ]: 858 : if (flushing) {
4172 : : /* step 1, now that we have the PREROLL lock, clear our unlock request */
4173 [ + + ]: 429 : if (bclass->unlock_stop)
4174 : 7 : bclass->unlock_stop (basesink);
4175 : :
4176 : : /* set need_preroll before we unblock the clock. If the clock is unblocked
4177 : : * before timing out, we can reuse the buffer for preroll. */
4178 : 429 : basesink->need_preroll = TRUE;
4179 : :
4180 : : /* step 2, unblock clock sync (if any) or any other blocking thing */
4181 [ + + ]: 429 : if (basesink->clock_id) {
4182 : 1 : gst_clock_id_unschedule (basesink->clock_id);
4183 : : }
4184 : :
4185 : : /* flush out the data thread if it's locked in finish_preroll, this will
4186 : : * also flush out the EOS state */
4187 [ - + ]: 429 : GST_DEBUG_OBJECT (basesink,
4188 : : "flushing out data thread, need preroll to TRUE");
4189 : 429 : gst_base_sink_preroll_queue_flush (basesink, pad);
4190 : : }
4191 : 858 : GST_PAD_PREROLL_UNLOCK (pad);
4192 : :
4193 : 858 : return TRUE;
4194 : : }
4195 : :
4196 : : static gboolean
4197 : 12 : gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active)
4198 : : {
4199 : : gboolean result;
4200 : :
4201 [ + + ]: 12 : if (active) {
4202 : : /* start task */
4203 : 6 : result = gst_pad_start_task (basesink->sinkpad,
4204 : 6 : (GstTaskFunction) gst_base_sink_loop, basesink->sinkpad);
4205 : : } else {
4206 : : /* step 2, make sure streaming finishes */
4207 : 6 : result = gst_pad_stop_task (basesink->sinkpad);
4208 : : }
4209 : :
4210 : 12 : return result;
4211 : : }
4212 : :
4213 : : static gboolean
4214 : 424 : gst_base_sink_pad_activate (GstPad * pad)
4215 : : {
4216 : 424 : gboolean result = FALSE;
4217 : : GstBaseSink *basesink;
4218 : :
4219 : 424 : basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
4220 : :
4221 [ - + ]: 424 : GST_DEBUG_OBJECT (basesink, "Trying pull mode first");
4222 : :
4223 : 424 : gst_base_sink_set_flushing (basesink, pad, FALSE);
4224 : :
4225 : : /* we need to have the pull mode enabled */
4226 [ + + ]: 424 : if (!basesink->can_activate_pull) {
4227 [ - + ]: 418 : GST_DEBUG_OBJECT (basesink, "pull mode disabled");
4228 : 418 : goto fallback;
4229 : : }
4230 : :
4231 : : /* check if downstreams supports pull mode at all */
4232 [ - + ]: 6 : if (!gst_pad_check_pull_range (pad)) {
4233 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "pull mode not supported");
4234 : 0 : goto fallback;
4235 : : }
4236 : :
4237 : : /* set the pad mode before starting the task so that it's in the
4238 : : * correct state for the new thread. also the sink set_caps and get_caps
4239 : : * function checks this */
4240 : 6 : basesink->pad_mode = GST_ACTIVATE_PULL;
4241 : :
4242 : : /* we first try to negotiate a format so that when we try to activate
4243 : : * downstream, it knows about our format */
4244 [ - + ]: 6 : if (!gst_base_sink_negotiate_pull (basesink)) {
4245 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "failed to negotiate in pull mode");
4246 : 0 : goto fallback;
4247 : : }
4248 : :
4249 : : /* ok activate now */
4250 [ - + ]: 6 : if (!gst_pad_activate_pull (pad, TRUE)) {
4251 : : /* clear any pending caps */
4252 : 0 : GST_OBJECT_LOCK (basesink);
4253 : 0 : gst_caps_replace (&basesink->priv->pull_caps, NULL);
4254 : 0 : GST_OBJECT_UNLOCK (basesink);
4255 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "failed to activate in pull mode");
4256 : 0 : goto fallback;
4257 : : }
4258 : :
4259 [ - + ]: 6 : GST_DEBUG_OBJECT (basesink, "Success activating pull mode");
4260 : 6 : result = TRUE;
4261 : 6 : goto done;
4262 : :
4263 : : /* push mode fallback */
4264 : : fallback:
4265 [ - + ]: 418 : GST_DEBUG_OBJECT (basesink, "Falling back to push mode");
4266 [ + - ]: 418 : if ((result = gst_pad_activate_push (pad, TRUE))) {
4267 [ - + ]: 418 : GST_DEBUG_OBJECT (basesink, "Success activating push mode");
4268 : : }
4269 : :
4270 : : done:
4271 [ - + ]: 424 : if (!result) {
4272 [ # # ]: 0 : GST_WARNING_OBJECT (basesink, "Could not activate pad in either mode");
4273 : 0 : gst_base_sink_set_flushing (basesink, pad, TRUE);
4274 : : }
4275 : :
4276 : 424 : gst_object_unref (basesink);
4277 : :
4278 : 424 : return result;
4279 : : }
4280 : :
4281 : : static gboolean
4282 : 836 : gst_base_sink_pad_activate_push (GstPad * pad, gboolean active)
4283 : : {
4284 : : gboolean result;
4285 : : GstBaseSink *basesink;
4286 : :
4287 : 836 : basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
4288 : :
4289 [ + + ]: 836 : if (active) {
4290 [ - + ]: 418 : if (!basesink->can_activate_push) {
4291 : 0 : result = FALSE;
4292 : 0 : basesink->pad_mode = GST_ACTIVATE_NONE;
4293 : : } else {
4294 : 418 : result = TRUE;
4295 : 418 : basesink->pad_mode = GST_ACTIVATE_PUSH;
4296 : : }
4297 : : } else {
4298 [ - + ]: 418 : if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PUSH)) {
4299 : 0 : g_warning ("Internal GStreamer activation error!!!");
4300 : 0 : result = FALSE;
4301 : : } else {
4302 : 418 : gst_base_sink_set_flushing (basesink, pad, TRUE);
4303 : 418 : result = TRUE;
4304 : 418 : basesink->pad_mode = GST_ACTIVATE_NONE;
4305 : : }
4306 : : }
4307 : :
4308 : 836 : gst_object_unref (basesink);
4309 : :
4310 : 836 : return result;
4311 : : }
4312 : :
4313 : : static gboolean
4314 : 6 : gst_base_sink_negotiate_pull (GstBaseSink * basesink)
4315 : : {
4316 : : GstCaps *caps;
4317 : : gboolean result;
4318 : :
4319 : 6 : result = FALSE;
4320 : :
4321 : : /* this returns the intersection between our caps and the peer caps. If there
4322 : : * is no peer, it returns NULL and we can't operate in pull mode so we can
4323 : : * fail the negotiation. */
4324 : 6 : caps = gst_pad_get_allowed_caps (GST_BASE_SINK_PAD (basesink));
4325 [ + - ][ + - ]: 6 : if (caps == NULL || gst_caps_is_empty (caps))
4326 : : goto no_caps_possible;
4327 : :
4328 [ - + ]: 6 : GST_DEBUG_OBJECT (basesink, "allowed caps: %" GST_PTR_FORMAT, caps);
4329 : :
4330 : 6 : caps = gst_caps_make_writable (caps);
4331 : : /* get the first (prefered) format */
4332 : 6 : gst_caps_truncate (caps);
4333 : : /* try to fixate */
4334 : 6 : gst_pad_fixate_caps (GST_BASE_SINK_PAD (basesink), caps);
4335 : :
4336 [ - + ]: 6 : GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
4337 : :
4338 [ + - ]: 6 : if (gst_caps_is_any (caps)) {
4339 [ - + ]: 6 : GST_DEBUG_OBJECT (basesink, "caps were ANY after fixating, "
4340 : : "allowing pull()");
4341 : : /* neither side has template caps in this case, so they are prepared for
4342 : : pull() without setcaps() */
4343 : 6 : result = TRUE;
4344 [ # # ]: 0 : } else if (gst_caps_is_fixed (caps)) {
4345 [ # # ]: 0 : if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
4346 : 0 : goto could_not_set_caps;
4347 : :
4348 : 0 : GST_OBJECT_LOCK (basesink);
4349 : 0 : gst_caps_replace (&basesink->priv->pull_caps, caps);
4350 : 0 : GST_OBJECT_UNLOCK (basesink);
4351 : :
4352 : 0 : result = TRUE;
4353 : : }
4354 : :
4355 : 6 : gst_caps_unref (caps);
4356 : :
4357 : 6 : return result;
4358 : :
4359 : : no_caps_possible:
4360 : : {
4361 [ # # ]: 0 : GST_INFO_OBJECT (basesink, "Pipeline could not agree on caps");
4362 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "get_allowed_caps() returned EMPTY");
4363 [ # # ]: 0 : if (caps)
4364 : 0 : gst_caps_unref (caps);
4365 : 0 : return FALSE;
4366 : : }
4367 : : could_not_set_caps:
4368 : : {
4369 [ # # ]: 0 : GST_INFO_OBJECT (basesink, "Could not set caps: %" GST_PTR_FORMAT, caps);
4370 : 0 : gst_caps_unref (caps);
4371 : 6 : return FALSE;
4372 : : }
4373 : : }
4374 : :
4375 : : /* this won't get called until we implement an activate function */
4376 : : static gboolean
4377 : 12 : gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active)
4378 : : {
4379 : 12 : gboolean result = FALSE;
4380 : : GstBaseSink *basesink;
4381 : : GstBaseSinkClass *bclass;
4382 : :
4383 : 12 : basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
4384 : 12 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
4385 : :
4386 [ + + ]: 12 : if (active) {
4387 : : GstFormat format;
4388 : : gint64 duration;
4389 : :
4390 : : /* we mark we have a newsegment here because pull based
4391 : : * mode works just fine without having a newsegment before the
4392 : : * first buffer */
4393 : 6 : format = GST_FORMAT_BYTES;
4394 : :
4395 : 6 : gst_segment_init (&basesink->segment, format);
4396 : 6 : gst_segment_init (basesink->abidata.ABI.clip_segment, format);
4397 : 6 : GST_OBJECT_LOCK (basesink);
4398 : 6 : basesink->have_newsegment = TRUE;
4399 : 6 : GST_OBJECT_UNLOCK (basesink);
4400 : :
4401 : : /* get the peer duration in bytes */
4402 : 6 : result = gst_pad_query_peer_duration (pad, &format, &duration);
4403 [ + - ]: 6 : if (result) {
4404 [ - + ]: 6 : GST_DEBUG_OBJECT (basesink,
4405 : : "setting duration in bytes to %" G_GINT64_FORMAT, duration);
4406 : 6 : gst_segment_set_duration (basesink->abidata.ABI.clip_segment, format,
4407 : : duration);
4408 : 6 : gst_segment_set_duration (&basesink->segment, format, duration);
4409 : : } else {
4410 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "unknown duration");
4411 : : }
4412 : :
4413 [ + - ]: 6 : if (bclass->activate_pull)
4414 : 6 : result = bclass->activate_pull (basesink, TRUE);
4415 : : else
4416 : 0 : result = FALSE;
4417 : :
4418 [ - + ]: 6 : if (!result)
4419 : 0 : goto activate_failed;
4420 : :
4421 : : } else {
4422 [ - + ]: 6 : if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PULL)) {
4423 : 0 : g_warning ("Internal GStreamer activation error!!!");
4424 : 0 : result = FALSE;
4425 : : } else {
4426 : 6 : result = gst_base_sink_set_flushing (basesink, pad, TRUE);
4427 [ + - ]: 6 : if (bclass->activate_pull)
4428 : 6 : result &= bclass->activate_pull (basesink, FALSE);
4429 : 6 : basesink->pad_mode = GST_ACTIVATE_NONE;
4430 : : /* clear any pending caps */
4431 : 6 : GST_OBJECT_LOCK (basesink);
4432 : 6 : gst_caps_replace (&basesink->priv->pull_caps, NULL);
4433 : 6 : GST_OBJECT_UNLOCK (basesink);
4434 : : }
4435 : : }
4436 : 12 : gst_object_unref (basesink);
4437 : :
4438 : 12 : return result;
4439 : :
4440 : : /* ERRORS */
4441 : : activate_failed:
4442 : : {
4443 : : /* reset, as starting the thread failed */
4444 : 0 : basesink->pad_mode = GST_ACTIVATE_NONE;
4445 : :
4446 [ # # ]: 0 : GST_ERROR_OBJECT (basesink, "subclass failed to activate in pull mode");
4447 : 12 : return FALSE;
4448 : : }
4449 : : }
4450 : :
4451 : : /* send an event to our sinkpad peer. */
4452 : : static gboolean
4453 : 112 : gst_base_sink_send_event (GstElement * element, GstEvent * event)
4454 : : {
4455 : : GstPad *pad;
4456 : 112 : GstBaseSink *basesink = GST_BASE_SINK (element);
4457 : 112 : gboolean forward, result = TRUE;
4458 : : GstActivateMode mode;
4459 : :
4460 : 112 : GST_OBJECT_LOCK (element);
4461 : : /* get the pad and the scheduling mode */
4462 : 112 : pad = gst_object_ref (basesink->sinkpad);
4463 : 112 : mode = basesink->pad_mode;
4464 : 112 : GST_OBJECT_UNLOCK (element);
4465 : :
4466 : : /* only push UPSTREAM events upstream */
4467 : 112 : forward = GST_EVENT_IS_UPSTREAM (event);
4468 : :
4469 [ - + ]: 112 : GST_DEBUG_OBJECT (basesink, "handling event %p %" GST_PTR_FORMAT, event,
4470 : : event);
4471 : :
4472 [ + + - - ]: 112 : switch (GST_EVENT_TYPE (event)) {
4473 : : case GST_EVENT_LATENCY:
4474 : : {
4475 : : GstClockTime latency;
4476 : :
4477 : 110 : gst_event_parse_latency (event, &latency);
4478 : :
4479 : : /* store the latency. We use this to adjust the running_time before syncing
4480 : : * it to the clock. */
4481 : 110 : GST_OBJECT_LOCK (element);
4482 : 110 : basesink->priv->latency = latency;
4483 [ + + ]: 110 : if (!basesink->priv->have_latency)
4484 : 8 : forward = FALSE;
4485 : 110 : GST_OBJECT_UNLOCK (element);
4486 [ - + ][ # # ]: 110 : GST_DEBUG_OBJECT (basesink, "latency set to %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
4487 : : GST_TIME_ARGS (latency));
4488 : :
4489 : : /* We forward this event so that all elements know about the global pipeline
4490 : : * latency. This is interesting for an element when it wants to figure out
4491 : : * when a particular piece of data will be rendered. */
4492 : 110 : break;
4493 : : }
4494 : : case GST_EVENT_SEEK:
4495 : : /* in pull mode we will execute the seek */
4496 [ - + ]: 2 : if (mode == GST_ACTIVATE_PULL)
4497 : 0 : result = gst_base_sink_perform_seek (basesink, pad, event);
4498 : 2 : break;
4499 : : case GST_EVENT_STEP:
4500 : 0 : result = gst_base_sink_perform_step (basesink, pad, event);
4501 : 0 : forward = FALSE;
4502 : 0 : break;
4503 : : default:
4504 : 0 : break;
4505 : : }
4506 : :
4507 [ + + ]: 112 : if (forward) {
4508 : 104 : result = gst_pad_push_event (pad, event);
4509 : : } else {
4510 : : /* not forwarded, unref the event */
4511 : 8 : gst_event_unref (event);
4512 : : }
4513 : :
4514 : 112 : gst_object_unref (pad);
4515 : 112 : return result;
4516 : : }
4517 : :
4518 : : static gboolean
4519 : 32 : gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
4520 : : gint64 * cur, gboolean * upstream)
4521 : : {
4522 : 32 : GstClock *clock = NULL;
4523 : 32 : gboolean res = FALSE;
4524 : : GstFormat oformat, tformat;
4525 : : GstSegment *segment;
4526 : : GstClockTime now, latency;
4527 : : GstClockTimeDiff base;
4528 : : gint64 time, accum, duration;
4529 : : gdouble rate;
4530 : : gint64 last;
4531 : : gboolean last_seen, with_clock, in_paused;
4532 : :
4533 : 32 : GST_OBJECT_LOCK (basesink);
4534 : : /* we can only get the segment when we are not NULL or READY */
4535 [ + + ]: 32 : if (!basesink->have_newsegment)
4536 : 7 : goto wrong_state;
4537 : :
4538 : 25 : in_paused = FALSE;
4539 : : /* when not in PLAYING or when we're busy with a state change, we
4540 : : * cannot read from the clock so we report time based on the
4541 : : * last seen timestamp. */
4542 [ + + ][ + + ]: 25 : if (GST_STATE (basesink) != GST_STATE_PLAYING ||
4543 : 13 : GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) {
4544 : 13 : in_paused = TRUE;
4545 : : }
4546 : :
4547 : : /* we don't use the clip segment in pull mode, when seeking we update the
4548 : : * main segment directly with the new segment values without it having to be
4549 : : * activated by the rendering after preroll */
4550 [ + - ]: 25 : if (basesink->pad_mode == GST_ACTIVATE_PUSH)
4551 : 25 : segment = basesink->abidata.ABI.clip_segment;
4552 : : else
4553 : 0 : segment = &basesink->segment;
4554 : :
4555 : : /* our intermediate time format */
4556 : 25 : tformat = GST_FORMAT_TIME;
4557 : : /* get the format in the segment */
4558 : 25 : oformat = segment->format;
4559 : :
4560 : : /* report with last seen position when EOS */
4561 : 25 : last_seen = basesink->eos;
4562 : :
4563 : : /* assume we will use the clock for getting the current position */
4564 : 25 : with_clock = TRUE;
4565 [ - + ]: 25 : if (basesink->sync == FALSE)
4566 : 0 : with_clock = FALSE;
4567 : :
4568 : : /* and we need a clock */
4569 [ + + ]: 25 : if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
4570 : 12 : with_clock = FALSE;
4571 : : else
4572 : 13 : gst_object_ref (clock);
4573 : :
4574 : : /* collect all data we need holding the lock */
4575 [ + - ]: 25 : if (GST_CLOCK_TIME_IS_VALID (segment->time))
4576 : 25 : time = segment->time;
4577 : : else
4578 : 0 : time = 0;
4579 : :
4580 [ + + ]: 25 : if (GST_CLOCK_TIME_IS_VALID (segment->stop))
4581 : 14 : duration = segment->stop - segment->start;
4582 : : else
4583 : 11 : duration = 0;
4584 : :
4585 : 25 : accum = segment->accum;
4586 : 25 : rate = segment->rate * segment->applied_rate;
4587 : 25 : latency = basesink->priv->latency;
4588 : :
4589 [ + + ]: 25 : if (oformat == GST_FORMAT_TIME) {
4590 : : gint64 start, stop;
4591 : :
4592 : 22 : start = basesink->priv->current_sstart;
4593 : 22 : stop = basesink->priv->current_sstop;
4594 : :
4595 [ + + ]: 22 : if (in_paused) {
4596 : : /* in paused we use the last position as a lower bound */
4597 [ + + ][ + - ]: 13 : if (stop == -1 || segment->rate > 0.0)
4598 : 13 : last = start;
4599 : : else
4600 : 13 : last = stop;
4601 : : } else {
4602 : : /* in playing, use last stop time as upper bound */
4603 [ + + ][ + - ]: 9 : if (start == -1 || segment->rate > 0.0)
4604 : 9 : last = stop;
4605 : : else
4606 : 22 : last = start;
4607 : : }
4608 : : } else {
4609 : : /* convert last stop to stream time */
4610 : 3 : last = gst_segment_to_stream_time (segment, oformat, segment->last_stop);
4611 : : }
4612 : :
4613 [ + + ]: 25 : if (in_paused) {
4614 : : /* in paused, use start_time */
4615 : 13 : base = GST_ELEMENT_START_TIME (basesink);
4616 [ - + ][ # # ]: 13 : GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
4617 : : GST_TIME_ARGS (base));
4618 [ + + ]: 12 : } else if (with_clock) {
4619 : : /* else use clock when needed */
4620 : 11 : base = GST_ELEMENT_CAST (basesink)->base_time;
4621 [ - + ][ # # ]: 11 : GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
4622 : : GST_TIME_ARGS (base));
4623 : : } else {
4624 : : /* else, no sync or clock -> no base time */
4625 [ - + ]: 1 : GST_DEBUG_OBJECT (basesink, "no sync or no clock");
4626 : 1 : base = -1;
4627 : : }
4628 : :
4629 : : /* no base, we can't calculate running_time, use last seem timestamp to report
4630 : : * time */
4631 [ + + ]: 25 : if (base == -1)
4632 : 1 : last_seen = TRUE;
4633 : :
4634 : : /* need to release the object lock before we can get the time,
4635 : : * a clock might take the LOCK of the provider, which could be
4636 : : * a basesink subclass. */
4637 : 25 : GST_OBJECT_UNLOCK (basesink);
4638 : :
4639 [ + + ]: 25 : if (last_seen) {
4640 : : /* in EOS or when no valid stream_time, report the value of last seen
4641 : : * timestamp */
4642 [ - + ]: 1 : if (last == -1) {
4643 : : /* no timestamp, we need to ask upstream */
4644 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "no last seen timestamp, asking upstream");
4645 : 0 : res = FALSE;
4646 : 0 : *upstream = TRUE;
4647 : 0 : goto done;
4648 : : }
4649 [ - + ][ # # ]: 1 : GST_DEBUG_OBJECT (basesink, "using last seen timestamp %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
4650 : : GST_TIME_ARGS (last));
4651 : 1 : *cur = last;
4652 : : } else {
4653 [ + + ]: 24 : if (oformat != tformat) {
4654 : : /* convert accum, time and duration to time */
4655 [ + - ]: 3 : if (!gst_pad_query_convert (basesink->sinkpad, oformat, accum, &tformat,
4656 : : &accum))
4657 : 3 : goto convert_failed;
4658 [ # # ]: 0 : if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration,
4659 : : &tformat, &duration))
4660 : 0 : goto convert_failed;
4661 [ # # ]: 0 : if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, &tformat,
4662 : : &time))
4663 : 0 : goto convert_failed;
4664 [ # # ]: 0 : if (!gst_pad_query_convert (basesink->sinkpad, oformat, last, &tformat,
4665 : : &last))
4666 : 0 : goto convert_failed;
4667 : :
4668 : : /* assume time format from now on */
4669 : 0 : oformat = tformat;
4670 : : }
4671 : :
4672 [ + + ][ + - ]: 21 : if (!in_paused && with_clock) {
4673 : 8 : now = gst_clock_get_time (clock);
4674 : : } else {
4675 : 13 : now = base;
4676 : 13 : base = 0;
4677 : : }
4678 : :
4679 : : /* subtract base time and accumulated time from the clock time.
4680 : : * Make sure we don't go negative. This is the current time in
4681 : : * the segment which we need to scale with the combined
4682 : : * rate and applied rate. */
4683 : 21 : base += accum;
4684 : 21 : base += latency;
4685 [ - + ]: 21 : if (GST_CLOCK_DIFF (base, now) < 0)
4686 : 0 : base = now;
4687 : :
4688 : : /* for negative rates we need to count back from the segment
4689 : : * duration. */
4690 [ - + ]: 21 : if (rate < 0.0)
4691 : 0 : time += duration;
4692 : :
4693 : 21 : *cur = time + gst_guint64_to_gdouble (now - base) * rate;
4694 : :
4695 [ + + ]: 21 : if (in_paused) {
4696 : : /* never report less than segment values in paused */
4697 [ + + ]: 13 : if (last != -1)
4698 : 2 : *cur = MAX (last, *cur);
4699 : : } else {
4700 : : /* never report more than last seen position in playing */
4701 [ + + ]: 8 : if (last != -1)
4702 : 7 : *cur = MIN (last, *cur);
4703 : : }
4704 : :
4705 [ - + ][ # # ]: 21 : GST_DEBUG_OBJECT (basesink,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
4706 : : "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %"
4707 : : GST_TIME_FORMAT " + time %" GST_TIME_FORMAT " last %" GST_TIME_FORMAT,
4708 : : GST_TIME_ARGS (now), GST_TIME_ARGS (base), GST_TIME_ARGS (accum),
4709 : : GST_TIME_ARGS (time), GST_TIME_ARGS (last));
4710 : : }
4711 : :
4712 [ - + ]: 22 : if (oformat != format) {
4713 : : /* convert to final format */
4714 [ # # ]: 0 : if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, &format, cur))
4715 : 0 : goto convert_failed;
4716 : : }
4717 : :
4718 : 22 : res = TRUE;
4719 : :
4720 : : done:
4721 [ - + ][ # # ]: 32 : GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
4722 : : res, GST_TIME_ARGS (*cur));
4723 : :
4724 [ + + ]: 32 : if (clock)
4725 : 13 : gst_object_unref (clock);
4726 : :
4727 : 32 : return res;
4728 : :
4729 : : /* special cases */
4730 : : wrong_state:
4731 : : {
4732 : : /* in NULL or READY we always return FALSE and -1 */
4733 [ - + ]: 7 : GST_DEBUG_OBJECT (basesink, "position in wrong state, return -1");
4734 : 7 : res = FALSE;
4735 : 7 : *cur = -1;
4736 : 7 : GST_OBJECT_UNLOCK (basesink);
4737 : 7 : goto done;
4738 : : }
4739 : : convert_failed:
4740 : : {
4741 [ - + ]: 3 : GST_DEBUG_OBJECT (basesink, "convert failed, try upstream");
4742 : 3 : *upstream = TRUE;
4743 : 3 : res = FALSE;
4744 : 3 : goto done;
4745 : : }
4746 : : }
4747 : :
4748 : : static gboolean
4749 : 1 : gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format,
4750 : : gint64 * dur, gboolean * upstream)
4751 : : {
4752 : 1 : gboolean res = FALSE;
4753 : :
4754 [ - + ]: 1 : if (basesink->pad_mode == GST_ACTIVATE_PULL) {
4755 : 0 : GstFormat uformat = GST_FORMAT_BYTES;
4756 : : gint64 uduration;
4757 : :
4758 : : /* get the duration in bytes, in pull mode that's all we are sure to
4759 : : * know. We have to explicitly get this value from upstream instead of
4760 : : * using our cached value because it might change. Duration caching
4761 : : * should be done at a higher level. */
4762 : 0 : res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat, &uduration);
4763 [ # # ]: 0 : if (res) {
4764 : 0 : gst_segment_set_duration (&basesink->segment, uformat, uduration);
4765 [ # # ]: 0 : if (format != uformat) {
4766 : : /* convert to the requested format */
4767 : 0 : res = gst_pad_query_convert (basesink->sinkpad, uformat, uduration,
4768 : : &format, dur);
4769 : : } else {
4770 : 0 : *dur = uduration;
4771 : : }
4772 : : }
4773 : 0 : *upstream = FALSE;
4774 : : } else {
4775 : 1 : *upstream = TRUE;
4776 : : }
4777 : :
4778 : 1 : return res;
4779 : : }
4780 : :
4781 : : static const GstQueryType *
4782 : 0 : gst_base_sink_get_query_types (GstElement * element)
4783 : : {
4784 : : static const GstQueryType query_types[] = {
4785 : : GST_QUERY_DURATION,
4786 : : GST_QUERY_POSITION,
4787 : : GST_QUERY_SEGMENT,
4788 : : GST_QUERY_LATENCY,
4789 : : 0
4790 : : };
4791 : :
4792 : 0 : return query_types;
4793 : : }
4794 : :
4795 : : static gboolean
4796 : 151 : gst_base_sink_query (GstElement * element, GstQuery * query)
4797 : : {
4798 : 151 : gboolean res = FALSE;
4799 : :
4800 : 151 : GstBaseSink *basesink = GST_BASE_SINK (element);
4801 : :
4802 [ + + + - : 151 : switch (GST_QUERY_TYPE (query)) {
- - - ]
4803 : : case GST_QUERY_POSITION:
4804 : : {
4805 : 32 : gint64 cur = 0;
4806 : : GstFormat format;
4807 : 32 : gboolean upstream = FALSE;
4808 : :
4809 : 32 : gst_query_parse_position (query, &format, NULL);
4810 : :
4811 [ - + ]: 32 : GST_DEBUG_OBJECT (basesink, "position query in format %s",
4812 : : gst_format_get_name (format));
4813 : :
4814 : : /* first try to get the position based on the clock */
4815 [ + + ]: 32 : if ((res =
4816 : 32 : gst_base_sink_get_position (basesink, format, &cur, &upstream))) {
4817 : 22 : gst_query_set_position (query, format, cur);
4818 [ + + ]: 10 : } else if (upstream) {
4819 : : /* fallback to peer query */
4820 : 3 : res = gst_pad_peer_query (basesink->sinkpad, query);
4821 : : }
4822 [ + + ]: 32 : if (!res) {
4823 : : /* we can handle a few things if upstream failed */
4824 [ - + ]: 7 : if (format == GST_FORMAT_PERCENT) {
4825 : 0 : gint64 dur = 0;
4826 : 0 : GstFormat uformat = GST_FORMAT_TIME;
4827 : :
4828 : 0 : res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur,
4829 : : &upstream);
4830 [ # # ][ # # ]: 0 : if (!res && upstream) {
4831 : 0 : res = gst_pad_query_peer_position (basesink->sinkpad, &uformat,
4832 : : &cur);
4833 : : }
4834 [ # # ]: 0 : if (res) {
4835 : 0 : res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur,
4836 : : &upstream);
4837 [ # # ][ # # ]: 0 : if (!res && upstream) {
4838 : 0 : res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat,
4839 : : &dur);
4840 : : }
4841 : : }
4842 [ # # ]: 0 : if (res) {
4843 : : gint64 pos;
4844 : :
4845 : 0 : pos = gst_util_uint64_scale (100 * GST_FORMAT_PERCENT_SCALE, cur,
4846 : : dur);
4847 : 0 : gst_query_set_position (query, GST_FORMAT_PERCENT, pos);
4848 : : }
4849 : : }
4850 : : }
4851 : 32 : break;
4852 : : }
4853 : : case GST_QUERY_DURATION:
4854 : : {
4855 : 1 : gint64 dur = 0;
4856 : : GstFormat format;
4857 : 1 : gboolean upstream = FALSE;
4858 : :
4859 : 1 : gst_query_parse_duration (query, &format, NULL);
4860 : :
4861 [ - + ]: 1 : GST_DEBUG_OBJECT (basesink, "duration query in format %s",
4862 : : gst_format_get_name (format));
4863 : :
4864 [ - + ]: 1 : if ((res =
4865 : 1 : gst_base_sink_get_duration (basesink, format, &dur, &upstream))) {
4866 : 0 : gst_query_set_duration (query, format, dur);
4867 [ + - ]: 1 : } else if (upstream) {
4868 : : /* fallback to peer query */
4869 : 1 : res = gst_pad_peer_query (basesink->sinkpad, query);
4870 : : }
4871 [ - + ]: 1 : if (!res) {
4872 : : /* we can handle a few things if upstream failed */
4873 [ # # ]: 0 : if (format == GST_FORMAT_PERCENT) {
4874 : 0 : gst_query_set_duration (query, GST_FORMAT_PERCENT,
4875 : : GST_FORMAT_PERCENT_MAX);
4876 : 0 : res = TRUE;
4877 : : }
4878 : : }
4879 : 1 : break;
4880 : : }
4881 : : case GST_QUERY_LATENCY:
4882 : : {
4883 : : gboolean live, us_live;
4884 : : GstClockTime min, max;
4885 : :
4886 [ + + ]: 118 : if ((res = gst_base_sink_query_latency (basesink, &live, &us_live, &min,
4887 : : &max))) {
4888 : 110 : gst_query_set_latency (query, live, min, max);
4889 : : }
4890 : 118 : break;
4891 : : }
4892 : : case GST_QUERY_JITTER:
4893 : 0 : break;
4894 : : case GST_QUERY_RATE:
4895 : : /* gst_query_set_rate (query, basesink->segment_rate); */
4896 : 0 : res = TRUE;
4897 : 0 : break;
4898 : : case GST_QUERY_SEGMENT:
4899 : : {
4900 [ # # ]: 0 : if (basesink->pad_mode == GST_ACTIVATE_PULL) {
4901 : 0 : gst_query_set_segment (query, basesink->segment.rate,
4902 : : GST_FORMAT_TIME, basesink->segment.start, basesink->segment.stop);
4903 : 0 : res = TRUE;
4904 : : } else {
4905 : 0 : res = gst_pad_peer_query (basesink->sinkpad, query);
4906 : : }
4907 : 0 : break;
4908 : : }
4909 : : case GST_QUERY_SEEKING:
4910 : : case GST_QUERY_CONVERT:
4911 : : case GST_QUERY_FORMATS:
4912 : : default:
4913 : 0 : res = gst_pad_peer_query (basesink->sinkpad, query);
4914 : 0 : break;
4915 : : }
4916 [ - + ]: 151 : GST_DEBUG_OBJECT (basesink, "query %s returns %d",
4917 : : GST_QUERY_TYPE_NAME (query), res);
4918 : 151 : return res;
4919 : : }
4920 : :
4921 : : static GstStateChangeReturn
4922 : 1554 : gst_base_sink_change_state (GstElement * element, GstStateChange transition)
4923 : : {
4924 : 1554 : GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4925 : 1554 : GstBaseSink *basesink = GST_BASE_SINK (element);
4926 : : GstBaseSinkClass *bclass;
4927 : : GstBaseSinkPrivate *priv;
4928 : :
4929 : 1554 : priv = basesink->priv;
4930 : :
4931 : 1554 : bclass = GST_BASE_SINK_GET_CLASS (basesink);
4932 : :
4933 [ + + + + ]: 1554 : switch (transition) {
4934 : : case GST_STATE_CHANGE_NULL_TO_READY:
4935 [ + + ]: 250 : if (bclass->start)
4936 [ + + ]: 33 : if (!bclass->start (basesink))
4937 : 28 : goto start_failed;
4938 : 222 : break;
4939 : : case GST_STATE_CHANGE_READY_TO_PAUSED:
4940 : : /* need to complete preroll before this state change completes, there
4941 : : * is no data flow in READY so we can safely assume we need to preroll. */
4942 : 424 : GST_PAD_PREROLL_LOCK (basesink->sinkpad);
4943 [ - + ]: 424 : GST_DEBUG_OBJECT (basesink, "READY to PAUSED");
4944 : 424 : basesink->have_newsegment = FALSE;
4945 : 424 : gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
4946 : 424 : gst_segment_init (basesink->abidata.ABI.clip_segment,
4947 : : GST_FORMAT_UNDEFINED);
4948 : 424 : basesink->offset = 0;
4949 : 424 : basesink->have_preroll = FALSE;
4950 : 424 : priv->step_unlock = FALSE;
4951 : 424 : basesink->need_preroll = TRUE;
4952 : 424 : basesink->playing_async = TRUE;
4953 : 424 : priv->current_sstart = GST_CLOCK_TIME_NONE;
4954 : 424 : priv->current_sstop = GST_CLOCK_TIME_NONE;
4955 : 424 : priv->eos_rtime = GST_CLOCK_TIME_NONE;
4956 : 424 : priv->latency = 0;
4957 : 424 : basesink->eos = FALSE;
4958 : 424 : priv->received_eos = FALSE;
4959 : 424 : gst_base_sink_reset_qos (basesink);
4960 : 424 : priv->commited = FALSE;
4961 : 424 : priv->call_preroll = TRUE;
4962 : 424 : priv->current_step.valid = FALSE;
4963 : 424 : priv->pending_step.valid = FALSE;
4964 [ + + ]: 424 : if (priv->async_enabled) {
4965 [ - + ]: 422 : GST_DEBUG_OBJECT (basesink, "doing async state change");
4966 : : /* when async enabled, post async-start message and return ASYNC from
4967 : : * the state change function */
4968 : 422 : ret = GST_STATE_CHANGE_ASYNC;
4969 : 422 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
4970 : : gst_message_new_async_start (GST_OBJECT_CAST (basesink), FALSE));
4971 : : } else {
4972 : 2 : priv->have_latency = TRUE;
4973 : : }
4974 : 424 : GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
4975 : 424 : break;
4976 : : case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4977 : 114 : GST_PAD_PREROLL_LOCK (basesink->sinkpad);
4978 [ + + ]: 114 : if (!gst_base_sink_needs_preroll (basesink)) {
4979 [ - + ]: 113 : GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, don't need preroll");
4980 : : /* no preroll needed anymore now. */
4981 : 113 : basesink->playing_async = FALSE;
4982 : 113 : basesink->need_preroll = FALSE;
4983 [ - + ]: 113 : if (basesink->eos) {
4984 : : GstMessage *message;
4985 : :
4986 : : /* need to post EOS message here */
4987 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "Now posting EOS");
4988 : 0 : message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
4989 : 0 : gst_message_set_seqnum (message, basesink->priv->seqnum);
4990 : 0 : gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
4991 : : } else {
4992 [ - + ]: 113 : GST_DEBUG_OBJECT (basesink, "signal preroll");
4993 : 113 : GST_PAD_PREROLL_SIGNAL (basesink->sinkpad);
4994 : : }
4995 : : } else {
4996 [ - + ]: 1 : GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled");
4997 : 1 : basesink->need_preroll = TRUE;
4998 : 1 : basesink->playing_async = TRUE;
4999 : 1 : priv->call_preroll = TRUE;
5000 : 1 : priv->commited = FALSE;
5001 [ - + ]: 1 : if (priv->async_enabled) {
5002 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink, "doing async state change");
5003 : 0 : ret = GST_STATE_CHANGE_ASYNC;
5004 : 0 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
5005 : : gst_message_new_async_start (GST_OBJECT_CAST (basesink), FALSE));
5006 : : }
5007 : : }
5008 : 114 : GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
5009 : 114 : break;
5010 : : default:
5011 : 766 : break;
5012 : : }
5013 : :
5014 : : {
5015 : : GstStateChangeReturn bret;
5016 : :
5017 : 1526 : bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5018 [ - + ]: 1526 : if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
5019 : 0 : goto activate_failed;
5020 : : }
5021 : :
5022 [ + + + + ]: 1526 : switch (transition) {
5023 : : case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5024 [ - + ]: 114 : GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED");
5025 : : /* FIXME, make sure we cannot enter _render first */
5026 : :
5027 : : /* we need to call ::unlock before locking PREROLL_LOCK
5028 : : * since we lock it before going into ::render */
5029 [ - + ]: 114 : if (bclass->unlock)
5030 : 0 : bclass->unlock (basesink);
5031 : :
5032 : 114 : GST_PAD_PREROLL_LOCK (basesink->sinkpad);
5033 [ - + ]: 114 : GST_DEBUG_OBJECT (basesink, "got preroll lock");
5034 : : /* now that we have the PREROLL lock, clear our unlock request */
5035 [ - + ]: 114 : if (bclass->unlock_stop)
5036 : 0 : bclass->unlock_stop (basesink);
5037 : :
5038 : : /* we need preroll again and we set the flag before unlocking the clockid
5039 : : * because if the clockid is unlocked before a current buffer expired, we
5040 : : * can use that buffer to preroll with */
5041 : 114 : basesink->need_preroll = TRUE;
5042 : :
5043 [ + + ]: 114 : if (basesink->clock_id) {
5044 [ - + ]: 10 : GST_DEBUG_OBJECT (basesink, "unschedule clock");
5045 : 10 : gst_clock_id_unschedule (basesink->clock_id);
5046 : : }
5047 : :
5048 : : /* if we don't have a preroll buffer we need to wait for a preroll and
5049 : : * return ASYNC. */
5050 [ + + ]: 114 : if (!gst_base_sink_needs_preroll (basesink)) {
5051 [ - + ]: 40 : GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are prerolled");
5052 : 40 : basesink->playing_async = FALSE;
5053 : : } else {
5054 [ + + ]: 74 : if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) {
5055 [ - + ]: 3 : GST_DEBUG_OBJECT (basesink, "element is <= READY");
5056 : 3 : ret = GST_STATE_CHANGE_SUCCESS;
5057 : : } else {
5058 [ - + ]: 71 : GST_DEBUG_OBJECT (basesink,
5059 : : "PLAYING to PAUSED, we are not prerolled");
5060 : 71 : basesink->playing_async = TRUE;
5061 : 71 : priv->commited = FALSE;
5062 : 71 : priv->call_preroll = TRUE;
5063 [ + + ]: 71 : if (priv->async_enabled) {
5064 [ - + ]: 70 : GST_DEBUG_OBJECT (basesink, "doing async state change");
5065 : 70 : ret = GST_STATE_CHANGE_ASYNC;
5066 : 70 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
5067 : : gst_message_new_async_start (GST_OBJECT_CAST (basesink),
5068 : : FALSE));
5069 : : }
5070 : : }
5071 : : }
5072 [ - + ]: 114 : GST_DEBUG_OBJECT (basesink, "rendered: %" G_GUINT64_FORMAT
5073 : : ", dropped: %" G_GUINT64_FORMAT, priv->rendered, priv->dropped);
5074 : :
5075 : 114 : gst_base_sink_reset_qos (basesink);
5076 : 114 : GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
5077 : 114 : break;
5078 : : case GST_STATE_CHANGE_PAUSED_TO_READY:
5079 : 424 : GST_PAD_PREROLL_LOCK (basesink->sinkpad);
5080 : : /* start by reseting our position state with the object lock so that the
5081 : : * position query gets the right idea. We do this before we post the
5082 : : * messages so that the message handlers pick this up. */
5083 : 424 : GST_OBJECT_LOCK (basesink);
5084 : 424 : basesink->have_newsegment = FALSE;
5085 : 424 : priv->current_sstart = GST_CLOCK_TIME_NONE;
5086 : 424 : priv->current_sstop = GST_CLOCK_TIME_NONE;
5087 : 424 : priv->have_latency = FALSE;
5088 [ + + ]: 424 : if (priv->cached_clock_id) {
5089 : 7 : gst_clock_id_unref (priv->cached_clock_id);
5090 : 7 : priv->cached_clock_id = NULL;
5091 : : }
5092 : 424 : GST_OBJECT_UNLOCK (basesink);
5093 : :
5094 : 424 : gst_base_sink_set_last_buffer (basesink, NULL);
5095 : 424 : priv->call_preroll = FALSE;
5096 : :
5097 [ + + ]: 424 : if (!priv->commited) {
5098 [ + + ]: 319 : if (priv->async_enabled) {
5099 [ - + ]: 317 : GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done");
5100 : :
5101 : 317 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
5102 : : gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
5103 : : GST_STATE_PLAYING, GST_STATE_PAUSED, GST_STATE_READY));
5104 : :
5105 : 317 : gst_element_post_message (GST_ELEMENT_CAST (basesink),
5106 : : gst_message_new_async_done (GST_OBJECT_CAST (basesink)));
5107 : : }
5108 : 319 : priv->commited = TRUE;
5109 : : } else {
5110 [ - + ]: 105 : GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll");
5111 : : }
5112 : 424 : GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
5113 : 424 : break;
5114 : : case GST_STATE_CHANGE_READY_TO_NULL:
5115 [ + + ]: 221 : if (bclass->stop) {
5116 [ - + ]: 5 : if (!bclass->stop (basesink)) {
5117 [ # # ]: 0 : GST_WARNING_OBJECT (basesink, "failed to stop");
5118 : : }
5119 : : }
5120 : 221 : gst_base_sink_set_last_buffer (basesink, NULL);
5121 : 221 : priv->call_preroll = FALSE;
5122 : 221 : break;
5123 : : default:
5124 : 767 : break;
5125 : : }
5126 : :
5127 : 1526 : return ret;
5128 : :
5129 : : /* ERRORS */
5130 : : start_failed:
5131 : : {
5132 [ - + ]: 28 : GST_DEBUG_OBJECT (basesink, "failed to start");
5133 : 28 : return GST_STATE_CHANGE_FAILURE;
5134 : : }
5135 : : activate_failed:
5136 : : {
5137 [ # # ]: 0 : GST_DEBUG_OBJECT (basesink,
5138 : : "element failed to change states -- activation problem?");
5139 : 1554 : return GST_STATE_CHANGE_FAILURE;
5140 : : }
5141 : : }
|