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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * SECTION:gstbasesink
24 * @title: GstBaseSink
25 * @short_description: Base class for sink elements
26 * @see_also: #GstBaseTransform, #GstBaseSrc
27 *
28 * #GstBaseSink is the base class for sink elements in GStreamer, such as
29 * xvimagesink or filesink. It is a layer on top of #GstElement that provides a
30 * simplified interface to plugin writers. #GstBaseSink handles many details
31 * for you, for example: preroll, clock synchronization, state changes,
32 * activation in push or pull mode, and queries.
33 *
34 * In most cases, when writing sink elements, there is no need to implement
35 * class methods from #GstElement or to set functions on pads, because the
36 * #GstBaseSink infrastructure should be sufficient.
37 *
38 * #GstBaseSink provides support for exactly one sink pad, which should be
39 * named "sink". A sink implementation (subclass of #GstBaseSink) should
40 * install a pad template in its class_init function, like so:
41 * |[<!-- language="C" -->
42 * static void
43 * my_element_class_init (GstMyElementClass *klass)
44 * {
45 * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
46 *
47 * // sinktemplate should be a #GstStaticPadTemplate with direction
48 * // %GST_PAD_SINK and name "sink"
49 * gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
50 *
51 * gst_element_class_set_static_metadata (gstelement_class,
52 * "Sink name",
53 * "Sink",
54 * "My Sink element",
55 * "The author <my.sink@my.email>");
56 * }
57 * ]|
58 *
59 * #GstBaseSink will handle the prerolling correctly. This means that it will
60 * return %GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
61 * buffer arrives in this element. The base class will call the
62 * #GstBaseSinkClass::preroll vmethod with this preroll buffer and will then
63 * commit the state change to the next asynchronously pending state.
64 *
65 * When the element is set to PLAYING, #GstBaseSink will synchronise on the
66 * clock using the times returned from #GstBaseSinkClass::get_times. If this
67 * function returns %GST_CLOCK_TIME_NONE for the start time, no synchronisation
68 * will be done. Synchronisation can be disabled entirely by setting the object
69 * #GstBaseSink:sync property to %FALSE.
70 *
71 * After synchronisation the virtual method #GstBaseSinkClass::render will be
72 * called. Subclasses should minimally implement this method.
73 *
74 * Subclasses that synchronise on the clock in the #GstBaseSinkClass::render
75 * method are supported as well. These classes typically receive a buffer in
76 * the render method and can then potentially block on the clock while
77 * rendering. A typical example is an audiosink.
78 * These subclasses can use gst_base_sink_wait_preroll() to perform the
79 * blocking wait.
80 *
81 * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait
82 * for the clock to reach the time indicated by the stop time of the last
83 * #GstBaseSinkClass::get_times call before posting an EOS message. When the
84 * element receives EOS in PAUSED, preroll completes, the event is queued and an
85 * EOS message is posted when going to PLAYING.
86 *
87 * #GstBaseSink will internally use the %GST_EVENT_SEGMENT events to schedule
88 * synchronisation and clipping of buffers. Buffers that fall completely outside
89 * of the current segment are dropped. Buffers that fall partially in the
90 * segment are rendered (and prerolled). Subclasses should do any subbuffer
91 * clipping themselves when needed.
92 *
93 * #GstBaseSink will by default report the current playback position in
94 * %GST_FORMAT_TIME based on the current clock time and segment information.
95 * If no clock has been set on the element, the query will be forwarded
96 * upstream.
97 *
98 * The #GstBaseSinkClass::set_caps function will be called when the subclass
99 * should configure itself to process a specific media type.
100 *
101 * The #GstBaseSinkClass::start and #GstBaseSinkClass::stop virtual methods
102 * will be called when resources should be allocated. Any
103 * #GstBaseSinkClass::preroll, #GstBaseSinkClass::render and
104 * #GstBaseSinkClass::set_caps function will be called between the
105 * #GstBaseSinkClass::start and #GstBaseSinkClass::stop calls.
106 *
107 * The #GstBaseSinkClass::event virtual method will be called when an event is
108 * received by #GstBaseSink. Normally this method should only be overridden by
109 * very specific elements (such as file sinks) which need to handle the
110 * newsegment event specially.
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 * The #GstBaseSink:async property can be used to instruct the sink to never
139 * perform an ASYNC state change. This feature is mostly usable when dealing
140 * with non-synchronized streams or sparse streams.
141 */
142
143 #ifdef HAVE_CONFIG_H
144 # include "config.h"
145 #endif
146
147 #include <gst/gst_private.h>
148
149 #include "gstbasesink.h"
150 #include <gst/gst-i18n-lib.h>
151 #ifdef OHOS_OPT_PERFORMANCE
152 // ohos.opt.performance.0005
153 // add trace
154 #include "gst_trace.h"
155 #endif
156
157 GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
158 #define GST_CAT_DEFAULT gst_base_sink_debug
159
160 #define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
161
162 typedef struct
163 {
164 gboolean valid; /* if this info is valid */
165 guint32 seqnum; /* the seqnum of the STEP event */
166 GstFormat format; /* the format of the amount */
167 guint64 amount; /* the total amount of data to skip */
168 guint64 position; /* the position in the stepped data */
169 guint64 duration; /* the duration in time of the skipped data */
170 guint64 start; /* running_time of the start */
171 gdouble rate; /* rate of skipping */
172 gdouble start_rate; /* rate before skipping */
173 guint64 start_start; /* start position skipping */
174 guint64 start_stop; /* stop position skipping */
175 gboolean flush; /* if this was a flushing step */
176 gboolean intermediate; /* if this is an intermediate step */
177 gboolean need_preroll; /* if we need preroll after this step */
178 } GstStepInfo;
179
180 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: define sink type
181 typedef enum {
182 SINK_TYPE_VIDEO = 0,
183 SINK_TYPE_AUDIO,
184 SINK_TYPE_UNKNOWN,
185 } SINK_TYPE;
186 #endif
187
188 struct _GstBaseSinkPrivate
189 {
190 gint qos_enabled; /* ATOMIC */
191 gboolean async_enabled;
192 GstClockTimeDiff ts_offset;
193 GstClockTime render_delay;
194 GstClockTime processing_deadline;
195
196 /* start, stop of current buffer, stream time, used to report position */
197 GstClockTime current_sstart;
198 GstClockTime current_sstop;
199
200 /* start, stop and jitter of current buffer, running time */
201 GstClockTime current_rstart;
202 GstClockTime current_rstop;
203 GstClockTimeDiff current_jitter;
204 /* the running time of the previous buffer */
205 GstClockTime prev_rstart;
206
207 /* EOS sync time in running time */
208 GstClockTime eos_rtime;
209
210 /* last buffer that arrived in time, running time */
211 GstClockTime last_render_time;
212 /* when the last buffer left the sink, running time */
213 GstClockTime last_left;
214
215 /* running averages go here these are done on running time */
216 GstClockTime avg_pt, avg_in_diff;
217 gdouble avg_rate; /* average with infinite window */
218
219 /* number of rendered and dropped frames */
220 guint64 rendered;
221 guint64 dropped;
222
223 /* latency stuff */
224 GstClockTime latency;
225
226 /* if we already committed the state */
227 gboolean committed;
228 /* state change to playing ongoing */
229 gboolean to_playing;
230
231 /* when we received EOS */
232 gboolean received_eos;
233
234 /* when we are prerolled and able to report latency */
235 gboolean have_latency;
236
237 /* the last buffer we prerolled or rendered. Useful for making snapshots */
238 gint enable_last_sample; /* atomic */
239 GstBuffer *last_buffer;
240 GstCaps *last_caps;
241 GstBufferList *last_buffer_list;
242
243 /* negotiated caps */
244 GstCaps *caps;
245
246 /* blocksize for pulling */
247 guint blocksize;
248
249 gboolean discont;
250
251 /* seqnum of the stream */
252 guint32 seqnum;
253
254 gboolean call_preroll;
255 gboolean step_unlock;
256
257 /* we have a pending and a current step operation */
258 GstStepInfo current_step;
259 GstStepInfo pending_step;
260
261 /* instant rate change state */
262 /* seqnum of the last instant-rate-sync-time event
263 * received. %GST_SEQNUM_INVALID if there isn't one */
264 guint32 instant_rate_sync_seqnum;
265 /* Active instant-rate multipler. 0.0 if nothing pending */
266 gdouble instant_rate_multiplier;
267 /* seqnum of the last instant-rate event.
268 * %GST_SEQNUM_INVALID if there isn't one */
269 guint32 last_instant_rate_seqnum;
270 guint32 segment_seqnum;
271 GstSegment upstream_segment;
272 /* Running time at the start of the last segment event
273 * or instant-rate switch in *our* segment, not upstream */
274 GstClockTime last_anchor_running_time;
275 /* Difference between upstream running time and our own running time
276 * at the last segment event or instant-rate switch:
277 * upstream + offset = ours */
278 GstClockTimeDiff instant_rate_offset;
279
280 /* Cached GstClockID */
281 GstClockID cached_clock_id;
282
283 /* for throttling and QoS */
284 GstClockTime earliest_in_time;
285 GstClockTime throttle_time;
286
287 /* for rate control */
288 guint64 max_bitrate;
289 GstClockTime rc_time;
290 GstClockTime rc_next;
291 gsize rc_accumulated;
292
293 gboolean drop_out_of_segment;
294 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
295 gboolean has_render_first_frame;
296 gboolean has_recv_first_frame;
297 guint64 tmp_render_nums_fps;
298 gint64 tmp_time_fps;
299 gint64 kpi_last_render_time;
300 guint64 late_frames_nums;
301 SINK_TYPE sink_type;
302 #endif
303 };
304
305 #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
306
307 /* generic running average, this has a neutral window size */
308 #define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
309
310 /* the windows for these running averages are experimentally obtained.
311 * positive values get averaged more while negative values use a small
312 * window so we can react faster to badness. */
313 #define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
314 #define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
315
316 /* BaseSink properties */
317
318 #define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
319 #define DEFAULT_CAN_ACTIVATE_PUSH TRUE
320
321 #define DEFAULT_SYNC TRUE
322 #define DEFAULT_MAX_LATENESS -1
323 #define DEFAULT_QOS FALSE
324 #define DEFAULT_ASYNC TRUE
325 #define DEFAULT_TS_OFFSET 0
326 #define DEFAULT_BLOCKSIZE 4096
327 #define DEFAULT_RENDER_DELAY 0
328 #define DEFAULT_ENABLE_LAST_SAMPLE TRUE
329 #define DEFAULT_THROTTLE_TIME 0
330 #define DEFAULT_MAX_BITRATE 0
331 #define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
332 #define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND)
333
334 enum
335 {
336 PROP_0,
337 PROP_SYNC,
338 PROP_MAX_LATENESS,
339 PROP_QOS,
340 PROP_ASYNC,
341 PROP_TS_OFFSET,
342 PROP_ENABLE_LAST_SAMPLE,
343 PROP_LAST_SAMPLE,
344 PROP_BLOCKSIZE,
345 PROP_RENDER_DELAY,
346 PROP_THROTTLE_TIME,
347 PROP_MAX_BITRATE,
348 PROP_PROCESSING_DEADLINE,
349 PROP_STATS,
350 PROP_LAST
351 };
352
353 static GstElementClass *parent_class = NULL;
354 static gint private_offset = 0;
355
356 static void gst_base_sink_class_init (GstBaseSinkClass * klass);
357 static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);
358 static void gst_base_sink_finalize (GObject * object);
359
360 GType
gst_base_sink_get_type(void)361 gst_base_sink_get_type (void)
362 {
363 static gsize base_sink_type = 0;
364
365 if (g_once_init_enter (&base_sink_type)) {
366 GType _type;
367 static const GTypeInfo base_sink_info = {
368 sizeof (GstBaseSinkClass),
369 NULL,
370 NULL,
371 (GClassInitFunc) gst_base_sink_class_init,
372 NULL,
373 NULL,
374 sizeof (GstBaseSink),
375 0,
376 (GInstanceInitFunc) gst_base_sink_init,
377 };
378
379 _type = g_type_register_static (GST_TYPE_ELEMENT,
380 "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
381
382 private_offset =
383 g_type_add_instance_private (_type, sizeof (GstBaseSinkPrivate));
384
385 g_once_init_leave (&base_sink_type, _type);
386 }
387 return base_sink_type;
388 }
389
390 static inline GstBaseSinkPrivate *
gst_base_sink_get_instance_private(GstBaseSink * self)391 gst_base_sink_get_instance_private (GstBaseSink * self)
392 {
393 return (G_STRUCT_MEMBER_P (self, private_offset));
394 }
395
396 static void gst_base_sink_set_property (GObject * object, guint prop_id,
397 const GValue * value, GParamSpec * pspec);
398 static void gst_base_sink_get_property (GObject * object, guint prop_id,
399 GValue * value, GParamSpec * pspec);
400
401 static gboolean gst_base_sink_send_event (GstElement * element,
402 GstEvent * event);
403 static gboolean default_element_query (GstElement * element, GstQuery * query);
404
405 static GstCaps *gst_base_sink_default_get_caps (GstBaseSink * sink,
406 GstCaps * caps);
407 static gboolean gst_base_sink_default_set_caps (GstBaseSink * sink,
408 GstCaps * caps);
409 static void gst_base_sink_default_get_times (GstBaseSink * basesink,
410 GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
411 static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
412 GstPad * pad, gboolean flushing);
413 static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
414 gboolean active);
415 static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink,
416 GstSegment * segment);
417 static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
418 GstEvent * event, GstSegment * segment);
419
420 static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
421 GstStateChange transition);
422
423 static gboolean gst_base_sink_sink_query (GstPad * pad, GstObject * parent,
424 GstQuery * query);
425 static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstObject * parent,
426 GstBuffer * buffer);
427 static GstFlowReturn gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
428 GstBufferList * list);
429
430 static void gst_base_sink_loop (GstPad * pad);
431 static gboolean gst_base_sink_pad_activate (GstPad * pad, GstObject * parent);
432 static gboolean gst_base_sink_pad_activate_mode (GstPad * pad,
433 GstObject * parent, GstPadMode mode, gboolean active);
434 static gboolean gst_base_sink_default_event (GstBaseSink * basesink,
435 GstEvent * event);
436 static GstFlowReturn gst_base_sink_default_wait_event (GstBaseSink * basesink,
437 GstEvent * event);
438 static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent,
439 GstEvent * event);
440
441 static gboolean gst_base_sink_default_query (GstBaseSink * sink,
442 GstQuery * query);
443
444 static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
445 static GstCaps *gst_base_sink_default_fixate (GstBaseSink * bsink,
446 GstCaps * caps);
447 static GstCaps *gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
448
449 /* check if an object was too late */
450 static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
451 GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop,
452 GstClockReturn status, GstClockTimeDiff jitter, gboolean render);
453
454 /* #ifdef OHOS_OPT_PERFORMANCE:comment out this macro to avoid c file differences caused by macros
455 * ohos.opt.performance.0002: update reach time for avsync
456 */
457 static GstClockTime gst_base_sink_default_update_reach_time (GstBaseSink * sink,
458 GstClockTime reach_time, gboolean *need_drop_this_buffer);
459 /* #endif */
460
461 static void
gst_base_sink_class_init(GstBaseSinkClass * klass)462 gst_base_sink_class_init (GstBaseSinkClass * klass)
463 {
464 GObjectClass *gobject_class;
465 GstElementClass *gstelement_class;
466
467 gobject_class = G_OBJECT_CLASS (klass);
468 gstelement_class = GST_ELEMENT_CLASS (klass);
469
470 if (private_offset != 0)
471 g_type_class_adjust_private_offset (klass, &private_offset);
472
473 GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
474 "basesink element");
475
476 parent_class = g_type_class_peek_parent (klass);
477
478 gobject_class->finalize = gst_base_sink_finalize;
479 gobject_class->set_property = gst_base_sink_set_property;
480 gobject_class->get_property = gst_base_sink_get_property;
481
482 g_object_class_install_property (gobject_class, PROP_SYNC,
483 g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
484 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
485
486 g_object_class_install_property (gobject_class, PROP_MAX_LATENESS,
487 g_param_spec_int64 ("max-lateness", "Max Lateness",
488 "Maximum number of nanoseconds that a buffer can be late before it "
489 "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS,
490 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
491
492 g_object_class_install_property (gobject_class, PROP_QOS,
493 g_param_spec_boolean ("qos", "Qos",
494 "Generate Quality-of-Service events upstream", DEFAULT_QOS,
495 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496 /**
497 * GstBaseSink:async:
498 *
499 * If set to %TRUE, the basesink will perform asynchronous state changes.
500 * When set to %FALSE, the sink will not signal the parent when it prerolls.
501 * Use this option when dealing with sparse streams or when synchronisation is
502 * not required.
503 */
504 g_object_class_install_property (gobject_class, PROP_ASYNC,
505 g_param_spec_boolean ("async", "Async",
506 "Go asynchronously to PAUSED", DEFAULT_ASYNC,
507 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
508 /**
509 * GstBaseSink:ts-offset:
510 *
511 * Controls the final synchronisation, a negative value will render the buffer
512 * earlier while a positive value delays playback. This property can be
513 * used to fix synchronisation in bad files.
514 */
515 g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
516 g_param_spec_int64 ("ts-offset", "TS Offset",
517 "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64,
518 DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
519
520 /**
521 * GstBaseSink:enable-last-sample:
522 *
523 * Enable the last-sample property. If %FALSE, basesink doesn't keep a
524 * reference to the last buffer arrived and the last-sample property is always
525 * set to %NULL. This can be useful if you need buffers to be released as soon
526 * as possible, eg. if you're using a buffer pool.
527 */
528 g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
529 g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer",
530 "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE,
531 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
532
533 /**
534 * GstBaseSink:last-sample:
535 *
536 * The last buffer that arrived in the sink and was used for preroll or for
537 * rendering. This property can be used to generate thumbnails. This property
538 * can be %NULL when the sink has not yet received a buffer.
539 */
540 g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE,
541 g_param_spec_boxed ("last-sample", "Last Sample",
542 "The last sample received in the sink", GST_TYPE_SAMPLE,
543 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
544 /**
545 * GstBaseSink:blocksize:
546 *
547 * The amount of bytes to pull when operating in pull mode.
548 */
549 /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
550 g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
551 g_param_spec_uint ("blocksize", "Block size",
552 "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
553 DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
554 /**
555 * GstBaseSink:render-delay:
556 *
557 * The additional delay between synchronisation and actual rendering of the
558 * media. This property will add additional latency to the device in order to
559 * make other sinks compensate for the delay.
560 */
561 g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
562 g_param_spec_uint64 ("render-delay", "Render Delay",
563 "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64,
564 DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
565 /**
566 * GstBaseSink:throttle-time:
567 *
568 * The time to insert between buffers. This property can be used to control
569 * the maximum amount of buffers per second to render. Setting this property
570 * to a value bigger than 0 will make the sink create THROTTLE QoS events.
571 */
572 g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
573 g_param_spec_uint64 ("throttle-time", "Throttle time",
574 "The time to keep between rendered buffers (0 = disabled)", 0,
575 G_MAXUINT64, DEFAULT_THROTTLE_TIME,
576 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 /**
578 * GstBaseSink:max-bitrate:
579 *
580 * Control the maximum amount of bits that will be rendered per second.
581 * Setting this property to a value bigger than 0 will make the sink delay
582 * rendering of the buffers when it would exceed to max-bitrate.
583 *
584 * Since: 1.2
585 */
586 g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
587 g_param_spec_uint64 ("max-bitrate", "Max Bitrate",
588 "The maximum bits per second to render (0 = disabled)", 0,
589 G_MAXUINT64, DEFAULT_MAX_BITRATE,
590 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
591 /**
592 * GstBaseSink:processing-deadline:
593 *
594 * Maximum amount of time (in nanoseconds) that the pipeline can take
595 * for processing the buffer. This is added to the latency of live
596 * pipelines.
597 *
598 * Since: 1.16
599 */
600 g_object_class_install_property (gobject_class, PROP_PROCESSING_DEADLINE,
601 g_param_spec_uint64 ("processing-deadline", "Processing deadline",
602 "Maximum processing time for a buffer in nanoseconds", 0,
603 G_MAXUINT64, DEFAULT_PROCESSING_DEADLINE,
604 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
605 /**
606 * GstBaseSink:stats:
607 *
608 * Various #GstBaseSink statistics. This property returns a #GstStructure
609 * with name `application/x-gst-base-sink-stats` with the following fields:
610 *
611 * - "average-rate" G_TYPE_DOUBLE average frame rate
612 * - "dropped" G_TYPE_UINT64 Number of dropped frames
613 * - "rendered" G_TYPE_UINT64 Number of rendered frames
614 *
615 * Since: 1.18
616 */
617 g_object_class_install_property (gobject_class, PROP_STATS,
618 g_param_spec_boxed ("stats", "Statistics",
619 "Sink Statistics", GST_TYPE_STRUCTURE,
620 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
621
622 gstelement_class->change_state =
623 GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
624 gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
625 gstelement_class->query = GST_DEBUG_FUNCPTR (default_element_query);
626
627 klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_caps);
628 klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_set_caps);
629 klass->fixate = GST_DEBUG_FUNCPTR (gst_base_sink_default_fixate);
630 klass->activate_pull =
631 GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
632 klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times);
633 klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query);
634 klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event);
635 klass->wait_event = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_event);
636 /* #ifdef OHOS_OPT_PERFORMANCE:comment out this macro to avoid c file differences caused by macros
637 * ohos.opt.performance.0002: update reach time for avsync
638 */
639 klass->update_reach_time = GST_DEBUG_FUNCPTR (gst_base_sink_default_update_reach_time);
640 /* #endif */
641
642 /* Registering debug symbols for function pointers */
643 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_fixate);
644 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate);
645 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_mode);
646 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event);
647 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain);
648 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list);
649 GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_sink_query);
650 }
651
652 static GstCaps *
gst_base_sink_query_caps(GstBaseSink * bsink,GstPad * pad,GstCaps * filter)653 gst_base_sink_query_caps (GstBaseSink * bsink, GstPad * pad, GstCaps * filter)
654 {
655 GstBaseSinkClass *bclass;
656 GstCaps *caps = NULL;
657 gboolean fixed;
658
659 bclass = GST_BASE_SINK_GET_CLASS (bsink);
660 fixed = GST_PAD_IS_FIXED_CAPS (pad);
661
662 if (fixed || bsink->pad_mode == GST_PAD_MODE_PULL) {
663 /* if we are operating in pull mode or fixed caps, we only accept the
664 * currently negotiated caps */
665 caps = gst_pad_get_current_caps (pad);
666 }
667 if (caps == NULL) {
668 if (bclass->get_caps)
669 caps = bclass->get_caps (bsink, filter);
670
671 if (caps == NULL) {
672 GstPadTemplate *pad_template;
673
674 pad_template =
675 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass),
676 "sink");
677 if (pad_template != NULL) {
678 caps = gst_pad_template_get_caps (pad_template);
679
680 if (filter) {
681 GstCaps *intersection;
682
683 intersection =
684 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
685 gst_caps_unref (caps);
686 caps = intersection;
687 }
688 }
689 }
690 }
691
692 return caps;
693 }
694
695 static GstCaps *
gst_base_sink_default_fixate(GstBaseSink * bsink,GstCaps * caps)696 gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps)
697 {
698 GST_DEBUG_OBJECT (bsink, "using default caps fixate function");
699 return gst_caps_fixate (caps);
700 }
701
702 static GstCaps *
gst_base_sink_fixate(GstBaseSink * bsink,GstCaps * caps)703 gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
704 {
705 GstBaseSinkClass *bclass;
706
707 bclass = GST_BASE_SINK_GET_CLASS (bsink);
708
709 if (bclass->fixate)
710 caps = bclass->fixate (bsink, caps);
711
712 return caps;
713 }
714
715 static void
gst_base_sink_init(GstBaseSink * basesink,gpointer g_class)716 gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
717 {
718 GstPadTemplate *pad_template;
719 GstBaseSinkPrivate *priv;
720
721 basesink->priv = priv = gst_base_sink_get_instance_private (basesink);
722
723 pad_template =
724 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
725 g_return_if_fail (pad_template != NULL);
726
727 basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
728
729 gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate);
730 gst_pad_set_activatemode_function (basesink->sinkpad,
731 gst_base_sink_pad_activate_mode);
732 gst_pad_set_query_function (basesink->sinkpad, gst_base_sink_sink_query);
733 gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event);
734 gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain);
735 gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list);
736 gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
737
738 basesink->pad_mode = GST_PAD_MODE_NONE;
739 g_mutex_init (&basesink->preroll_lock);
740 g_cond_init (&basesink->preroll_cond);
741 priv->have_latency = FALSE;
742
743 basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
744 basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
745
746 basesink->sync = DEFAULT_SYNC;
747 basesink->max_lateness = DEFAULT_MAX_LATENESS;
748 g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS);
749 priv->async_enabled = DEFAULT_ASYNC;
750 priv->ts_offset = DEFAULT_TS_OFFSET;
751 priv->render_delay = DEFAULT_RENDER_DELAY;
752 priv->processing_deadline = DEFAULT_PROCESSING_DEADLINE;
753 priv->blocksize = DEFAULT_BLOCKSIZE;
754 priv->cached_clock_id = NULL;
755 g_atomic_int_set (&priv->enable_last_sample, DEFAULT_ENABLE_LAST_SAMPLE);
756 priv->throttle_time = DEFAULT_THROTTLE_TIME;
757 priv->max_bitrate = DEFAULT_MAX_BITRATE;
758
759 priv->drop_out_of_segment = DEFAULT_DROP_OUT_OF_SEGMENT;
760
761 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
762 priv->has_render_first_frame = FALSE;
763 priv->has_recv_first_frame = FALSE;
764 priv->tmp_render_nums_fps = 0;
765 priv->tmp_time_fps = 0;
766 priv->kpi_last_render_time = 0;
767 priv->late_frames_nums = 0;
768 #endif
769 GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_SINK);
770 }
771
772 static void
gst_base_sink_finalize(GObject * object)773 gst_base_sink_finalize (GObject * object)
774 {
775 GstBaseSink *basesink;
776
777 basesink = GST_BASE_SINK (object);
778
779 g_mutex_clear (&basesink->preroll_lock);
780 g_cond_clear (&basesink->preroll_cond);
781
782 G_OBJECT_CLASS (parent_class)->finalize (object);
783 }
784
785 /**
786 * gst_base_sink_set_sync:
787 * @sink: the sink
788 * @sync: the new sync value.
789 *
790 * Configures @sink to synchronize on the clock or not. When
791 * @sync is %FALSE, incoming samples will be played as fast as
792 * possible. If @sync is %TRUE, the timestamps of the incoming
793 * buffers will be used to schedule the exact render time of its
794 * contents.
795 */
796 void
gst_base_sink_set_sync(GstBaseSink * sink,gboolean sync)797 gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
798 {
799 g_return_if_fail (GST_IS_BASE_SINK (sink));
800
801 GST_OBJECT_LOCK (sink);
802 sink->sync = sync;
803 GST_OBJECT_UNLOCK (sink);
804 }
805
806 /**
807 * gst_base_sink_get_sync:
808 * @sink: the sink
809 *
810 * Checks if @sink is currently configured to synchronize against the
811 * clock.
812 *
813 * Returns: %TRUE if the sink is configured to synchronize against the clock.
814 */
815 gboolean
gst_base_sink_get_sync(GstBaseSink * sink)816 gst_base_sink_get_sync (GstBaseSink * sink)
817 {
818 gboolean res;
819
820 g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
821
822 GST_OBJECT_LOCK (sink);
823 res = sink->sync;
824 GST_OBJECT_UNLOCK (sink);
825
826 return res;
827 }
828
829 /**
830 * gst_base_sink_set_drop_out_of_segment:
831 * @sink: the sink
832 * @drop_out_of_segment: drop buffers outside the segment
833 *
834 * Configure @sink to drop buffers which are outside the current segment
835 *
836 * Since: 1.12
837 */
838 void
gst_base_sink_set_drop_out_of_segment(GstBaseSink * sink,gboolean drop_out_of_segment)839 gst_base_sink_set_drop_out_of_segment (GstBaseSink * sink,
840 gboolean drop_out_of_segment)
841 {
842 g_return_if_fail (GST_IS_BASE_SINK (sink));
843
844 GST_OBJECT_LOCK (sink);
845 sink->priv->drop_out_of_segment = drop_out_of_segment;
846 GST_OBJECT_UNLOCK (sink);
847
848 }
849
850 /**
851 * gst_base_sink_get_drop_out_of_segment:
852 * @sink: the sink
853 *
854 * Checks if @sink is currently configured to drop buffers which are outside
855 * the current segment
856 *
857 * Returns: %TRUE if the sink is configured to drop buffers outside the
858 * current segment.
859 *
860 * Since: 1.12
861 */
862 gboolean
gst_base_sink_get_drop_out_of_segment(GstBaseSink * sink)863 gst_base_sink_get_drop_out_of_segment (GstBaseSink * sink)
864 {
865 gboolean res;
866
867 g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
868
869 GST_OBJECT_LOCK (sink);
870 res = sink->priv->drop_out_of_segment;
871 GST_OBJECT_UNLOCK (sink);
872
873 return res;
874 }
875
876 /**
877 * gst_base_sink_set_max_lateness:
878 * @sink: the sink
879 * @max_lateness: the new max lateness value.
880 *
881 * Sets the new max lateness value to @max_lateness. This value is
882 * used to decide if a buffer should be dropped or not based on the
883 * buffer timestamp and the current clock time. A value of -1 means
884 * an unlimited time.
885 */
886 void
gst_base_sink_set_max_lateness(GstBaseSink * sink,gint64 max_lateness)887 gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
888 {
889 g_return_if_fail (GST_IS_BASE_SINK (sink));
890
891 GST_OBJECT_LOCK (sink);
892 sink->max_lateness = max_lateness;
893 GST_OBJECT_UNLOCK (sink);
894 }
895
896 /**
897 * gst_base_sink_get_max_lateness:
898 * @sink: the sink
899 *
900 * Gets the max lateness value. See gst_base_sink_set_max_lateness() for
901 * more details.
902 *
903 * Returns: The maximum time in nanoseconds that a buffer can be late
904 * before it is dropped and not rendered. A value of -1 means an
905 * unlimited time.
906 */
907 gint64
gst_base_sink_get_max_lateness(GstBaseSink * sink)908 gst_base_sink_get_max_lateness (GstBaseSink * sink)
909 {
910 gint64 res;
911
912 g_return_val_if_fail (GST_IS_BASE_SINK (sink), -1);
913
914 GST_OBJECT_LOCK (sink);
915 res = sink->max_lateness;
916 GST_OBJECT_UNLOCK (sink);
917
918 return res;
919 }
920
921 /**
922 * gst_base_sink_set_qos_enabled:
923 * @sink: the sink
924 * @enabled: the new qos value.
925 *
926 * Configures @sink to send Quality-of-Service events upstream.
927 */
928 void
gst_base_sink_set_qos_enabled(GstBaseSink * sink,gboolean enabled)929 gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
930 {
931 g_return_if_fail (GST_IS_BASE_SINK (sink));
932
933 g_atomic_int_set (&sink->priv->qos_enabled, enabled);
934 }
935
936 /**
937 * gst_base_sink_is_qos_enabled:
938 * @sink: the sink
939 *
940 * Checks if @sink is currently configured to send Quality-of-Service events
941 * upstream.
942 *
943 * Returns: %TRUE if the sink is configured to perform Quality-of-Service.
944 */
945 gboolean
gst_base_sink_is_qos_enabled(GstBaseSink * sink)946 gst_base_sink_is_qos_enabled (GstBaseSink * sink)
947 {
948 gboolean res;
949
950 g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
951
952 res = g_atomic_int_get (&sink->priv->qos_enabled);
953
954 return res;
955 }
956
957 /**
958 * gst_base_sink_set_async_enabled:
959 * @sink: the sink
960 * @enabled: the new async value.
961 *
962 * Configures @sink to perform all state changes asynchronously. When async is
963 * disabled, the sink will immediately go to PAUSED instead of waiting for a
964 * preroll buffer. This feature is useful if the sink does not synchronize
965 * against the clock or when it is dealing with sparse streams.
966 */
967 void
gst_base_sink_set_async_enabled(GstBaseSink * sink,gboolean enabled)968 gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
969 {
970 g_return_if_fail (GST_IS_BASE_SINK (sink));
971
972 GST_BASE_SINK_PREROLL_LOCK (sink);
973 g_atomic_int_set (&sink->priv->async_enabled, enabled);
974 GST_LOG_OBJECT (sink, "set async enabled to %d", enabled);
975 GST_BASE_SINK_PREROLL_UNLOCK (sink);
976 }
977
978 /**
979 * gst_base_sink_is_async_enabled:
980 * @sink: the sink
981 *
982 * Checks if @sink is currently configured to perform asynchronous state
983 * changes to PAUSED.
984 *
985 * Returns: %TRUE if the sink is configured to perform asynchronous state
986 * changes.
987 */
988 gboolean
gst_base_sink_is_async_enabled(GstBaseSink * sink)989 gst_base_sink_is_async_enabled (GstBaseSink * sink)
990 {
991 gboolean res;
992
993 g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
994
995 res = g_atomic_int_get (&sink->priv->async_enabled);
996
997 return res;
998 }
999
1000 /**
1001 * gst_base_sink_set_ts_offset:
1002 * @sink: the sink
1003 * @offset: the new offset
1004 *
1005 * Adjust the synchronisation of @sink with @offset. A negative value will
1006 * render buffers earlier than their timestamp. A positive value will delay
1007 * rendering. This function can be used to fix playback of badly timestamped
1008 * buffers.
1009 */
1010 void
gst_base_sink_set_ts_offset(GstBaseSink * sink,GstClockTimeDiff offset)1011 gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
1012 {
1013 g_return_if_fail (GST_IS_BASE_SINK (sink));
1014
1015 GST_OBJECT_LOCK (sink);
1016 sink->priv->ts_offset = offset;
1017 GST_LOG_OBJECT (sink, "set time offset to %" G_GINT64_FORMAT, offset);
1018 GST_OBJECT_UNLOCK (sink);
1019 }
1020
1021 /**
1022 * gst_base_sink_get_ts_offset:
1023 * @sink: the sink
1024 *
1025 * Get the synchronisation offset of @sink.
1026 *
1027 * Returns: The synchronisation offset.
1028 */
1029 GstClockTimeDiff
gst_base_sink_get_ts_offset(GstBaseSink * sink)1030 gst_base_sink_get_ts_offset (GstBaseSink * sink)
1031 {
1032 GstClockTimeDiff res;
1033
1034 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1035
1036 GST_OBJECT_LOCK (sink);
1037 res = sink->priv->ts_offset;
1038 GST_OBJECT_UNLOCK (sink);
1039
1040 return res;
1041 }
1042
1043 /**
1044 * gst_base_sink_get_last_sample:
1045 * @sink: the sink
1046 *
1047 * Get the last sample that arrived in the sink and was used for preroll or for
1048 * rendering. This property can be used to generate thumbnails.
1049 *
1050 * The #GstCaps on the sample can be used to determine the type of the buffer.
1051 *
1052 * Free-function: gst_sample_unref
1053 *
1054 * Returns: (transfer full) (nullable): a #GstSample. gst_sample_unref() after
1055 * usage. This function returns %NULL when no buffer has arrived in the
1056 * sink yet or when the sink is not in PAUSED or PLAYING.
1057 */
1058 GstSample *
gst_base_sink_get_last_sample(GstBaseSink * sink)1059 gst_base_sink_get_last_sample (GstBaseSink * sink)
1060 {
1061 GstSample *res = NULL;
1062
1063 g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);
1064
1065 GST_OBJECT_LOCK (sink);
1066 if (sink->priv->last_buffer_list) {
1067 GstBuffer *first_buffer = NULL;
1068
1069 /* Set the first buffer in the list to last sample's buffer */
1070 first_buffer = gst_buffer_list_get (sink->priv->last_buffer_list, 0);
1071 res =
1072 gst_sample_new (first_buffer, sink->priv->last_caps, &sink->segment,
1073 NULL);
1074 gst_sample_set_buffer_list (res, sink->priv->last_buffer_list);
1075 } else if (sink->priv->last_buffer) {
1076 res = gst_sample_new (sink->priv->last_buffer,
1077 sink->priv->last_caps, &sink->segment, NULL);
1078 }
1079 GST_OBJECT_UNLOCK (sink);
1080
1081 return res;
1082 }
1083
1084 /* with OBJECT_LOCK */
1085 static void
gst_base_sink_set_last_buffer_unlocked(GstBaseSink * sink,GstBuffer * buffer)1086 gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
1087 {
1088 GstBuffer *old;
1089
1090 old = sink->priv->last_buffer;
1091 if (G_LIKELY (old != buffer)) {
1092 GST_DEBUG_OBJECT (sink, "setting last buffer to %p", buffer);
1093 if (G_LIKELY (buffer))
1094 gst_buffer_ref (buffer);
1095 sink->priv->last_buffer = buffer;
1096 if (buffer)
1097 /* copy over the caps */
1098 gst_caps_replace (&sink->priv->last_caps, sink->priv->caps);
1099 else
1100 gst_caps_replace (&sink->priv->last_caps, NULL);
1101 } else {
1102 old = NULL;
1103 }
1104 /* avoid unreffing with the lock because cleanup code might want to take the
1105 * lock too */
1106 if (G_LIKELY (old)) {
1107 GST_OBJECT_UNLOCK (sink);
1108 gst_buffer_unref (old);
1109 GST_OBJECT_LOCK (sink);
1110 }
1111 }
1112
1113 /* with OBJECT_LOCK */
1114 static void
gst_base_sink_set_last_buffer_list_unlocked(GstBaseSink * sink,GstBufferList * buffer_list)1115 gst_base_sink_set_last_buffer_list_unlocked (GstBaseSink * sink,
1116 GstBufferList * buffer_list)
1117 {
1118 GstBufferList *old;
1119
1120 old = sink->priv->last_buffer_list;
1121 if (G_LIKELY (old != buffer_list)) {
1122 GST_DEBUG_OBJECT (sink, "setting last buffer list to %p", buffer_list);
1123 if (G_LIKELY (buffer_list))
1124 gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer_list));
1125 sink->priv->last_buffer_list = buffer_list;
1126 } else {
1127 old = NULL;
1128 }
1129
1130 /* avoid unreffing with the lock because cleanup code might want to take the
1131 * lock too */
1132 if (G_LIKELY (old)) {
1133 GST_OBJECT_UNLOCK (sink);
1134 gst_mini_object_unref (GST_MINI_OBJECT_CAST (old));
1135 GST_OBJECT_LOCK (sink);
1136 }
1137 }
1138
1139 static void
gst_base_sink_set_last_buffer(GstBaseSink * sink,GstBuffer * buffer)1140 gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
1141 {
1142 if (!g_atomic_int_get (&sink->priv->enable_last_sample))
1143 return;
1144
1145 GST_OBJECT_LOCK (sink);
1146 gst_base_sink_set_last_buffer_unlocked (sink, buffer);
1147 GST_OBJECT_UNLOCK (sink);
1148 }
1149
1150 static void
gst_base_sink_set_last_buffer_list(GstBaseSink * sink,GstBufferList * buffer_list)1151 gst_base_sink_set_last_buffer_list (GstBaseSink * sink,
1152 GstBufferList * buffer_list)
1153 {
1154 if (!g_atomic_int_get (&sink->priv->enable_last_sample))
1155 return;
1156
1157 GST_OBJECT_LOCK (sink);
1158 gst_base_sink_set_last_buffer_list_unlocked (sink, buffer_list);
1159 GST_OBJECT_UNLOCK (sink);
1160 }
1161
1162 /**
1163 * gst_base_sink_set_last_sample_enabled:
1164 * @sink: the sink
1165 * @enabled: the new enable-last-sample value.
1166 *
1167 * Configures @sink to store the last received sample in the last-sample
1168 * property.
1169 */
1170 void
gst_base_sink_set_last_sample_enabled(GstBaseSink * sink,gboolean enabled)1171 gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
1172 {
1173 g_return_if_fail (GST_IS_BASE_SINK (sink));
1174
1175 /* Only take lock if we change the value */
1176 if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_sample,
1177 !enabled, enabled) && !enabled) {
1178 GST_OBJECT_LOCK (sink);
1179 gst_base_sink_set_last_buffer_unlocked (sink, NULL);
1180 gst_base_sink_set_last_buffer_list_unlocked (sink, NULL);
1181 GST_OBJECT_UNLOCK (sink);
1182 }
1183 }
1184
1185 /**
1186 * gst_base_sink_is_last_sample_enabled:
1187 * @sink: the sink
1188 *
1189 * Checks if @sink is currently configured to store the last received sample in
1190 * the last-sample property.
1191 *
1192 * Returns: %TRUE if the sink is configured to store the last received sample.
1193 */
1194 gboolean
gst_base_sink_is_last_sample_enabled(GstBaseSink * sink)1195 gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
1196 {
1197 g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
1198
1199 return g_atomic_int_get (&sink->priv->enable_last_sample);
1200 }
1201
1202 /**
1203 * gst_base_sink_get_latency:
1204 * @sink: the sink
1205 *
1206 * Get the currently configured latency.
1207 *
1208 * Returns: The configured latency.
1209 */
1210 GstClockTime
gst_base_sink_get_latency(GstBaseSink * sink)1211 gst_base_sink_get_latency (GstBaseSink * sink)
1212 {
1213 GstClockTime res;
1214
1215 GST_OBJECT_LOCK (sink);
1216 res = sink->priv->latency;
1217 GST_OBJECT_UNLOCK (sink);
1218
1219 return res;
1220 }
1221
1222 /**
1223 * gst_base_sink_query_latency:
1224 * @sink: the sink
1225 * @live: (out) (allow-none): if the sink is live
1226 * @upstream_live: (out) (allow-none): if an upstream element is live
1227 * @min_latency: (out) (allow-none): the min latency of the upstream elements
1228 * @max_latency: (out) (allow-none): the max latency of the upstream elements
1229 *
1230 * Query the sink for the latency parameters. The latency will be queried from
1231 * the upstream elements. @live will be %TRUE if @sink is configured to
1232 * synchronize against the clock. @upstream_live will be %TRUE if an upstream
1233 * element is live.
1234 *
1235 * If both @live and @upstream_live are %TRUE, the sink will want to compensate
1236 * for the latency introduced by the upstream elements by setting the
1237 * @min_latency to a strictly positive value.
1238 *
1239 * This function is mostly used by subclasses.
1240 *
1241 * Returns: %TRUE if the query succeeded.
1242 */
1243 gboolean
gst_base_sink_query_latency(GstBaseSink * sink,gboolean * live,gboolean * upstream_live,GstClockTime * min_latency,GstClockTime * max_latency)1244 gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
1245 gboolean * upstream_live, GstClockTime * min_latency,
1246 GstClockTime * max_latency)
1247 {
1248 gboolean l, us_live, res, have_latency;
1249 GstClockTime min, max, render_delay, processing_deadline;
1250 GstQuery *query;
1251 GstClockTime us_min, us_max;
1252
1253 /* we are live when we sync to the clock */
1254 GST_OBJECT_LOCK (sink);
1255 l = sink->sync;
1256 have_latency = sink->priv->have_latency;
1257 render_delay = sink->priv->render_delay;
1258 processing_deadline = sink->priv->processing_deadline;
1259 GST_OBJECT_UNLOCK (sink);
1260
1261 /* assume no latency */
1262 min = 0;
1263 max = -1;
1264 us_live = FALSE;
1265 us_min = 0;
1266 us_max = 0;
1267
1268 if (have_latency) {
1269 GST_DEBUG_OBJECT (sink, "we are ready for LATENCY query");
1270 /* we are ready for a latency query this is when we preroll or when we are
1271 * not async. */
1272 query = gst_query_new_latency ();
1273
1274 /* ask the peer for the latency */
1275 if ((res = gst_pad_peer_query (sink->sinkpad, query))) {
1276 /* get upstream min and max latency */
1277 gst_query_parse_latency (query, &us_live, &us_min, &us_max);
1278
1279 if (us_live) {
1280 /* upstream live, use its latency, subclasses should use these
1281 * values to create the complete latency. */
1282 min = us_min;
1283 max = us_max;
1284
1285 if (l) {
1286 if (max == -1 || min + processing_deadline <= max)
1287 min += processing_deadline;
1288 else {
1289 GST_ELEMENT_WARNING (sink, CORE, CLOCK,
1290 (_("Pipeline construction is invalid, please add queues.")),
1291 ("Not enough buffering available for "
1292 " the processing deadline of %" GST_TIME_FORMAT
1293 ", add enough queues to buffer %" GST_TIME_FORMAT
1294 " additional data. Shortening processing latency to %"
1295 GST_TIME_FORMAT ".",
1296 GST_TIME_ARGS (processing_deadline),
1297 GST_TIME_ARGS (min + processing_deadline - max),
1298 GST_TIME_ARGS (max - min)));
1299 min = max;
1300 }
1301 }
1302 }
1303 if (l) {
1304 /* we need to add the render delay if we are live */
1305 min += render_delay;
1306 if (max != -1)
1307 max += render_delay;
1308 }
1309 }
1310 gst_query_unref (query);
1311 } else {
1312 GST_DEBUG_OBJECT (sink, "we are not yet ready for LATENCY query");
1313 res = FALSE;
1314 }
1315
1316 /* not live, we tried to do the query, if it failed we return TRUE anyway */
1317 if (!res) {
1318 if (!l) {
1319 res = TRUE;
1320 GST_DEBUG_OBJECT (sink, "latency query failed but we are not live");
1321 } else {
1322 GST_DEBUG_OBJECT (sink, "latency query failed and we are live");
1323 }
1324 }
1325
1326 if (res) {
1327 GST_DEBUG_OBJECT (sink, "latency query: live: %d, have_latency %d,"
1328 " upstream_live %d, min(%" GST_TIME_FORMAT ")=upstream(%"
1329 GST_TIME_FORMAT ")+processing_deadline(%" GST_TIME_FORMAT
1330 ")+render_delay(%" GST_TIME_FORMAT "), max(%" GST_TIME_FORMAT
1331 ")=upstream(%" GST_TIME_FORMAT ")+render_delay(%" GST_TIME_FORMAT ")",
1332 l, have_latency, us_live, GST_TIME_ARGS (min), GST_TIME_ARGS (us_min),
1333 GST_TIME_ARGS (processing_deadline), GST_TIME_ARGS (render_delay),
1334 GST_TIME_ARGS (max), GST_TIME_ARGS (us_max),
1335 GST_TIME_ARGS (render_delay));
1336
1337 if (live)
1338 *live = l;
1339 if (upstream_live)
1340 *upstream_live = us_live;
1341 if (min_latency)
1342 *min_latency = min;
1343 if (max_latency)
1344 *max_latency = max;
1345 }
1346 return res;
1347 }
1348
1349 /**
1350 * gst_base_sink_set_render_delay:
1351 * @sink: a #GstBaseSink
1352 * @delay: the new delay
1353 *
1354 * Set the render delay in @sink to @delay. The render delay is the time
1355 * between actual rendering of a buffer and its synchronisation time. Some
1356 * devices might delay media rendering which can be compensated for with this
1357 * function.
1358 *
1359 * After calling this function, this sink will report additional latency and
1360 * other sinks will adjust their latency to delay the rendering of their media.
1361 *
1362 * This function is usually called by subclasses.
1363 */
1364 void
gst_base_sink_set_render_delay(GstBaseSink * sink,GstClockTime delay)1365 gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
1366 {
1367 GstClockTime old_render_delay;
1368
1369 g_return_if_fail (GST_IS_BASE_SINK (sink));
1370 g_return_if_fail (GST_CLOCK_TIME_IS_VALID (delay));
1371
1372 GST_OBJECT_LOCK (sink);
1373 old_render_delay = sink->priv->render_delay;
1374 sink->priv->render_delay = delay;
1375 GST_LOG_OBJECT (sink, "set render delay to %" GST_TIME_FORMAT,
1376 GST_TIME_ARGS (delay));
1377 GST_OBJECT_UNLOCK (sink);
1378
1379 if (delay != old_render_delay) {
1380 GST_DEBUG_OBJECT (sink, "posting latency changed");
1381 gst_element_post_message (GST_ELEMENT_CAST (sink),
1382 gst_message_new_latency (GST_OBJECT_CAST (sink)));
1383 }
1384 }
1385
1386 /**
1387 * gst_base_sink_get_render_delay:
1388 * @sink: a #GstBaseSink
1389 *
1390 * Get the render delay of @sink. see gst_base_sink_set_render_delay() for more
1391 * information about the render delay.
1392 *
1393 * Returns: the render delay of @sink.
1394 */
1395 GstClockTime
gst_base_sink_get_render_delay(GstBaseSink * sink)1396 gst_base_sink_get_render_delay (GstBaseSink * sink)
1397 {
1398 GstClockTimeDiff res;
1399
1400 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1401
1402 GST_OBJECT_LOCK (sink);
1403 res = sink->priv->render_delay;
1404 GST_OBJECT_UNLOCK (sink);
1405
1406 return res;
1407 }
1408
1409 /**
1410 * gst_base_sink_set_blocksize:
1411 * @sink: a #GstBaseSink
1412 * @blocksize: the blocksize in bytes
1413 *
1414 * Set the number of bytes that the sink will pull when it is operating in pull
1415 * mode.
1416 */
1417 /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
1418 void
gst_base_sink_set_blocksize(GstBaseSink * sink,guint blocksize)1419 gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
1420 {
1421 g_return_if_fail (GST_IS_BASE_SINK (sink));
1422
1423 GST_OBJECT_LOCK (sink);
1424 sink->priv->blocksize = blocksize;
1425 GST_LOG_OBJECT (sink, "set blocksize to %u", blocksize);
1426 GST_OBJECT_UNLOCK (sink);
1427 }
1428
1429 /**
1430 * gst_base_sink_get_blocksize:
1431 * @sink: a #GstBaseSink
1432 *
1433 * Get the number of bytes that the sink will pull when it is operating in pull
1434 * mode.
1435 *
1436 * Returns: the number of bytes @sink will pull in pull mode.
1437 */
1438 /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
1439 guint
gst_base_sink_get_blocksize(GstBaseSink * sink)1440 gst_base_sink_get_blocksize (GstBaseSink * sink)
1441 {
1442 guint res;
1443
1444 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1445
1446 GST_OBJECT_LOCK (sink);
1447 res = sink->priv->blocksize;
1448 GST_OBJECT_UNLOCK (sink);
1449
1450 return res;
1451 }
1452
1453 /**
1454 * gst_base_sink_set_throttle_time:
1455 * @sink: a #GstBaseSink
1456 * @throttle: the throttle time in nanoseconds
1457 *
1458 * Set the time that will be inserted between rendered buffers. This
1459 * can be used to control the maximum buffers per second that the sink
1460 * will render.
1461 */
1462 void
gst_base_sink_set_throttle_time(GstBaseSink * sink,guint64 throttle)1463 gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
1464 {
1465 g_return_if_fail (GST_IS_BASE_SINK (sink));
1466
1467 GST_OBJECT_LOCK (sink);
1468 sink->priv->throttle_time = throttle;
1469 GST_LOG_OBJECT (sink, "set throttle_time to %" G_GUINT64_FORMAT, throttle);
1470 GST_OBJECT_UNLOCK (sink);
1471 }
1472
1473 /**
1474 * gst_base_sink_get_throttle_time:
1475 * @sink: a #GstBaseSink
1476 *
1477 * Get the time that will be inserted between frames to control the
1478 * maximum buffers per second.
1479 *
1480 * Returns: the number of nanoseconds @sink will put between frames.
1481 */
1482 guint64
gst_base_sink_get_throttle_time(GstBaseSink * sink)1483 gst_base_sink_get_throttle_time (GstBaseSink * sink)
1484 {
1485 guint64 res;
1486
1487 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1488
1489 GST_OBJECT_LOCK (sink);
1490 res = sink->priv->throttle_time;
1491 GST_OBJECT_UNLOCK (sink);
1492
1493 return res;
1494 }
1495
1496 /**
1497 * gst_base_sink_set_max_bitrate:
1498 * @sink: a #GstBaseSink
1499 * @max_bitrate: the max_bitrate in bits per second
1500 *
1501 * Set the maximum amount of bits per second that the sink will render.
1502 *
1503 * Since: 1.2
1504 */
1505 void
gst_base_sink_set_max_bitrate(GstBaseSink * sink,guint64 max_bitrate)1506 gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate)
1507 {
1508 g_return_if_fail (GST_IS_BASE_SINK (sink));
1509
1510 GST_OBJECT_LOCK (sink);
1511 sink->priv->max_bitrate = max_bitrate;
1512 GST_LOG_OBJECT (sink, "set max_bitrate to %" G_GUINT64_FORMAT, max_bitrate);
1513 GST_OBJECT_UNLOCK (sink);
1514 }
1515
1516 /**
1517 * gst_base_sink_get_max_bitrate:
1518 * @sink: a #GstBaseSink
1519 *
1520 * Get the maximum amount of bits per second that the sink will render.
1521 *
1522 * Returns: the maximum number of bits per second @sink will render.
1523 *
1524 * Since: 1.2
1525 */
1526 guint64
gst_base_sink_get_max_bitrate(GstBaseSink * sink)1527 gst_base_sink_get_max_bitrate (GstBaseSink * sink)
1528 {
1529 guint64 res;
1530
1531 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1532
1533 GST_OBJECT_LOCK (sink);
1534 res = sink->priv->max_bitrate;
1535 GST_OBJECT_UNLOCK (sink);
1536
1537 return res;
1538 }
1539
1540 /**
1541 * gst_base_sink_set_processing_deadline:
1542 * @sink: a #GstBaseSink
1543 * @processing_deadline: the new processing deadline in nanoseconds.
1544 *
1545 * Maximum amount of time (in nanoseconds) that the pipeline can take
1546 * for processing the buffer. This is added to the latency of live
1547 * pipelines.
1548 *
1549 * This function is usually called by subclasses.
1550 *
1551 * Since: 1.16
1552 */
1553 void
gst_base_sink_set_processing_deadline(GstBaseSink * sink,GstClockTime processing_deadline)1554 gst_base_sink_set_processing_deadline (GstBaseSink * sink,
1555 GstClockTime processing_deadline)
1556 {
1557 GstClockTime old_processing_deadline;
1558
1559 g_return_if_fail (GST_IS_BASE_SINK (sink));
1560
1561 GST_OBJECT_LOCK (sink);
1562 old_processing_deadline = sink->priv->processing_deadline;
1563 sink->priv->processing_deadline = processing_deadline;
1564 GST_LOG_OBJECT (sink, "set render processing_deadline to %" GST_TIME_FORMAT,
1565 GST_TIME_ARGS (processing_deadline));
1566 GST_OBJECT_UNLOCK (sink);
1567
1568 if (processing_deadline != old_processing_deadline) {
1569 GST_DEBUG_OBJECT (sink, "posting latency changed");
1570 gst_element_post_message (GST_ELEMENT_CAST (sink),
1571 gst_message_new_latency (GST_OBJECT_CAST (sink)));
1572 }
1573 }
1574
1575 /**
1576 * gst_base_sink_get_processing_deadline:
1577 * @sink: a #GstBaseSink
1578 *
1579 * Get the processing deadline of @sink. see
1580 * gst_base_sink_set_processing_deadline() for more information about
1581 * the processing deadline.
1582 *
1583 * Returns: the processing deadline
1584 *
1585 * Since: 1.16
1586 */
1587 GstClockTime
gst_base_sink_get_processing_deadline(GstBaseSink * sink)1588 gst_base_sink_get_processing_deadline (GstBaseSink * sink)
1589 {
1590 GstClockTimeDiff res;
1591
1592 g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
1593
1594 GST_OBJECT_LOCK (sink);
1595 res = sink->priv->processing_deadline;
1596 GST_OBJECT_UNLOCK (sink);
1597
1598 return res;
1599 }
1600
1601 static void
gst_base_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1602 gst_base_sink_set_property (GObject * object, guint prop_id,
1603 const GValue * value, GParamSpec * pspec)
1604 {
1605 GstBaseSink *sink = GST_BASE_SINK (object);
1606
1607 switch (prop_id) {
1608 case PROP_SYNC:
1609 gst_base_sink_set_sync (sink, g_value_get_boolean (value));
1610 break;
1611 case PROP_MAX_LATENESS:
1612 gst_base_sink_set_max_lateness (sink, g_value_get_int64 (value));
1613 break;
1614 case PROP_QOS:
1615 gst_base_sink_set_qos_enabled (sink, g_value_get_boolean (value));
1616 break;
1617 case PROP_ASYNC:
1618 gst_base_sink_set_async_enabled (sink, g_value_get_boolean (value));
1619 break;
1620 case PROP_TS_OFFSET:
1621 gst_base_sink_set_ts_offset (sink, g_value_get_int64 (value));
1622 break;
1623 case PROP_BLOCKSIZE:
1624 gst_base_sink_set_blocksize (sink, g_value_get_uint (value));
1625 break;
1626 case PROP_RENDER_DELAY:
1627 gst_base_sink_set_render_delay (sink, g_value_get_uint64 (value));
1628 break;
1629 case PROP_ENABLE_LAST_SAMPLE:
1630 gst_base_sink_set_last_sample_enabled (sink, g_value_get_boolean (value));
1631 break;
1632 case PROP_THROTTLE_TIME:
1633 gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value));
1634 break;
1635 case PROP_MAX_BITRATE:
1636 gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value));
1637 break;
1638 case PROP_PROCESSING_DEADLINE:
1639 gst_base_sink_set_processing_deadline (sink, g_value_get_uint64 (value));
1640 break;
1641 default:
1642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1643 break;
1644 }
1645 }
1646
1647 static void
gst_base_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1648 gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
1649 GParamSpec * pspec)
1650 {
1651 GstBaseSink *sink = GST_BASE_SINK (object);
1652
1653 switch (prop_id) {
1654 case PROP_SYNC:
1655 g_value_set_boolean (value, gst_base_sink_get_sync (sink));
1656 break;
1657 case PROP_MAX_LATENESS:
1658 g_value_set_int64 (value, gst_base_sink_get_max_lateness (sink));
1659 break;
1660 case PROP_QOS:
1661 g_value_set_boolean (value, gst_base_sink_is_qos_enabled (sink));
1662 break;
1663 case PROP_ASYNC:
1664 g_value_set_boolean (value, gst_base_sink_is_async_enabled (sink));
1665 break;
1666 case PROP_TS_OFFSET:
1667 g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink));
1668 break;
1669 case PROP_LAST_SAMPLE:
1670 gst_value_take_sample (value, gst_base_sink_get_last_sample (sink));
1671 break;
1672 case PROP_ENABLE_LAST_SAMPLE:
1673 g_value_set_boolean (value, gst_base_sink_is_last_sample_enabled (sink));
1674 break;
1675 case PROP_BLOCKSIZE:
1676 g_value_set_uint (value, gst_base_sink_get_blocksize (sink));
1677 break;
1678 case PROP_RENDER_DELAY:
1679 g_value_set_uint64 (value, gst_base_sink_get_render_delay (sink));
1680 break;
1681 case PROP_THROTTLE_TIME:
1682 g_value_set_uint64 (value, gst_base_sink_get_throttle_time (sink));
1683 break;
1684 case PROP_MAX_BITRATE:
1685 g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink));
1686 break;
1687 case PROP_PROCESSING_DEADLINE:
1688 g_value_set_uint64 (value, gst_base_sink_get_processing_deadline (sink));
1689 break;
1690 case PROP_STATS:
1691 g_value_take_boxed (value, gst_base_sink_get_stats (sink));
1692 break;
1693 default:
1694 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1695 break;
1696 }
1697 }
1698
1699
1700 static GstCaps *
gst_base_sink_default_get_caps(GstBaseSink * sink,GstCaps * filter)1701 gst_base_sink_default_get_caps (GstBaseSink * sink, GstCaps * filter)
1702 {
1703 return NULL;
1704 }
1705
1706 static gboolean
gst_base_sink_default_set_caps(GstBaseSink * sink,GstCaps * caps)1707 gst_base_sink_default_set_caps (GstBaseSink * sink, GstCaps * caps)
1708 {
1709 return TRUE;
1710 }
1711
1712 /* #ifdef OHOS_OPT_PERFORMANCE: comment out this macro to avoid c file differences caused by macros
1713 * ohos.opt.performance.0002: update reach time for avsync
1714 */
1715 static GstClockTime
gst_base_sink_default_update_reach_time(GstBaseSink * sink,GstClockTime reach_time,gboolean * need_drop_this_buffer)1716 gst_base_sink_default_update_reach_time (GstBaseSink * sink, GstClockTime reach_time, gboolean *need_drop_this_buffer)
1717 {
1718 return reach_time;
1719 }
1720 /* #endif */
1721
1722 /* with PREROLL_LOCK, STREAM_LOCK */
1723 static gboolean
gst_base_sink_commit_state(GstBaseSink * basesink)1724 gst_base_sink_commit_state (GstBaseSink * basesink)
1725 {
1726 /* commit state and proceed to next pending state */
1727 GstState current, next, pending, post_pending;
1728 gboolean post_paused = FALSE;
1729 gboolean post_async_done = FALSE;
1730 gboolean post_playing = FALSE;
1731
1732 /* we are certainly not playing async anymore now */
1733 basesink->playing_async = FALSE;
1734
1735 GST_OBJECT_LOCK (basesink);
1736 current = GST_STATE (basesink);
1737 next = GST_STATE_NEXT (basesink);
1738 pending = GST_STATE_PENDING (basesink);
1739 post_pending = pending;
1740
1741 switch (pending) {
1742 case GST_STATE_PLAYING:
1743 {
1744 GST_DEBUG_OBJECT (basesink, "committing state to PLAYING");
1745
1746 basesink->need_preroll = FALSE;
1747 post_async_done = TRUE;
1748 basesink->priv->committed = TRUE;
1749 post_playing = TRUE;
1750 /* post PAUSED too when we were READY */
1751 if (current == GST_STATE_READY) {
1752 post_paused = TRUE;
1753 }
1754 break;
1755 }
1756 case GST_STATE_PAUSED:
1757 GST_DEBUG_OBJECT (basesink, "committing state to PAUSED");
1758 post_paused = TRUE;
1759 post_async_done = TRUE;
1760 basesink->priv->committed = TRUE;
1761 post_pending = GST_STATE_VOID_PENDING;
1762 break;
1763 case GST_STATE_READY:
1764 case GST_STATE_NULL:
1765 goto stopping;
1766 case GST_STATE_VOID_PENDING:
1767 goto nothing_pending;
1768 default:
1769 break;
1770 }
1771
1772 /* we can report latency queries now */
1773 basesink->priv->have_latency = TRUE;
1774
1775 GST_STATE (basesink) = pending;
1776 GST_STATE_NEXT (basesink) = GST_STATE_VOID_PENDING;
1777 GST_STATE_PENDING (basesink) = GST_STATE_VOID_PENDING;
1778 GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_SUCCESS;
1779 GST_OBJECT_UNLOCK (basesink);
1780
1781 if (post_paused) {
1782 GST_DEBUG_OBJECT (basesink, "posting PAUSED state change message");
1783 gst_element_post_message (GST_ELEMENT_CAST (basesink),
1784 gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
1785 current, next, post_pending));
1786 }
1787 if (post_async_done) {
1788 GST_DEBUG_OBJECT (basesink, "posting async-done message");
1789 gst_element_post_message (GST_ELEMENT_CAST (basesink),
1790 gst_message_new_async_done (GST_OBJECT_CAST (basesink),
1791 GST_CLOCK_TIME_NONE));
1792 }
1793 if (post_playing) {
1794 if (post_paused) {
1795 GstElementClass *klass;
1796
1797 klass = GST_ELEMENT_GET_CLASS (basesink);
1798 basesink->have_preroll = TRUE;
1799 /* after releasing this lock, the state change function
1800 * can execute concurrently with this thread. There is nothing we do to
1801 * prevent this for now. subclasses should be prepared to handle it. */
1802 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
1803
1804 if (klass->change_state)
1805 klass->change_state (GST_ELEMENT_CAST (basesink),
1806 GST_STATE_CHANGE_PAUSED_TO_PLAYING);
1807
1808 GST_BASE_SINK_PREROLL_LOCK (basesink);
1809 /* state change function could have been executed and we could be
1810 * flushing now */
1811 if (G_UNLIKELY (basesink->flushing))
1812 goto stopping_unlocked;
1813 }
1814 GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
1815 /* FIXME, we released the PREROLL lock above, it's possible that this
1816 * message is not correct anymore when the element went back to PAUSED */
1817 gst_element_post_message (GST_ELEMENT_CAST (basesink),
1818 gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
1819 next, pending, GST_STATE_VOID_PENDING));
1820 }
1821
1822 gst_element_post_message (GST_ELEMENT_CAST (basesink),
1823 gst_message_new_latency (GST_OBJECT_CAST (basesink)));
1824
1825 GST_STATE_BROADCAST (basesink);
1826
1827 return TRUE;
1828
1829 nothing_pending:
1830 {
1831 /* Depending on the state, set our vars. We get in this situation when the
1832 * state change function got a change to update the state vars before the
1833 * streaming thread did. This is fine but we need to make sure that we
1834 * update the need_preroll var since it was %TRUE when we got here and might
1835 * become %FALSE if we got to PLAYING. */
1836 GST_DEBUG_OBJECT (basesink, "nothing to commit, now in %s",
1837 gst_element_state_get_name (current));
1838 switch (current) {
1839 case GST_STATE_PLAYING:
1840 basesink->need_preroll = FALSE;
1841 break;
1842 case GST_STATE_PAUSED:
1843 basesink->need_preroll = TRUE;
1844 break;
1845 default:
1846 basesink->need_preroll = FALSE;
1847 basesink->flushing = TRUE;
1848 break;
1849 }
1850 /* we can report latency queries now */
1851 basesink->priv->have_latency = TRUE;
1852 GST_OBJECT_UNLOCK (basesink);
1853
1854 gst_element_post_message (GST_ELEMENT_CAST (basesink),
1855 gst_message_new_latency (GST_OBJECT_CAST (basesink)));
1856 return TRUE;
1857 }
1858 stopping_unlocked:
1859 {
1860 GST_OBJECT_LOCK (basesink);
1861 goto stopping;
1862 }
1863 stopping:
1864 {
1865 /* app is going to READY */
1866 GST_DEBUG_OBJECT (basesink, "stopping");
1867 basesink->need_preroll = FALSE;
1868 basesink->flushing = TRUE;
1869 GST_OBJECT_UNLOCK (basesink);
1870 return FALSE;
1871 }
1872 }
1873
1874 static void
start_stepping(GstBaseSink * sink,GstSegment * segment,GstStepInfo * pending,GstStepInfo * current)1875 start_stepping (GstBaseSink * sink, GstSegment * segment,
1876 GstStepInfo * pending, GstStepInfo * current)
1877 {
1878 gint64 end;
1879 GstMessage *message;
1880
1881 GST_DEBUG_OBJECT (sink, "update pending step");
1882
1883 GST_OBJECT_LOCK (sink);
1884 memcpy (current, pending, sizeof (GstStepInfo));
1885 pending->valid = FALSE;
1886 GST_OBJECT_UNLOCK (sink);
1887
1888 /* post message first */
1889 message =
1890 gst_message_new_step_start (GST_OBJECT (sink), TRUE, current->format,
1891 current->amount, current->rate, current->flush, current->intermediate);
1892 gst_message_set_seqnum (message, current->seqnum);
1893 gst_element_post_message (GST_ELEMENT (sink), message);
1894
1895 /* get the running time of where we paused and remember it */
1896 current->start = gst_element_get_start_time (GST_ELEMENT_CAST (sink));
1897 gst_segment_set_running_time (segment, GST_FORMAT_TIME, current->start);
1898
1899 /* set the new rate for the remainder of the segment */
1900 current->start_rate = segment->rate;
1901 segment->rate *= current->rate;
1902
1903 /* save values */
1904 if (segment->rate > 0.0)
1905 current->start_stop = segment->stop;
1906 else
1907 current->start_start = segment->start;
1908
1909 if (current->format == GST_FORMAT_TIME) {
1910 /* calculate the running-time when the step operation should stop */
1911 if (current->amount != -1)
1912 end = current->start + current->amount;
1913 else
1914 end = -1;
1915
1916 if (!current->flush) {
1917 gint64 position;
1918
1919 /* update the segment clipping regions for non-flushing seeks */
1920 if (segment->rate > 0.0) {
1921 if (end != -1)
1922 position =
1923 gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
1924 end);
1925 else
1926 position = segment->stop;
1927
1928 segment->stop = position;
1929 segment->position = position;
1930 } else {
1931 if (end != -1)
1932 position =
1933 gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
1934 end);
1935 else
1936 position = segment->start;
1937
1938 segment->time = position;
1939 segment->start = position;
1940 segment->position = position;
1941 }
1942 }
1943 }
1944
1945 GST_DEBUG_OBJECT (sink, "segment now %" GST_SEGMENT_FORMAT, segment);
1946 GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT,
1947 GST_TIME_ARGS (current->start));
1948
1949 GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
1950 "rate: %f", current->amount, gst_format_get_name (current->format),
1951 current->rate);
1952 }
1953
1954 static void
stop_stepping(GstBaseSink * sink,GstSegment * segment,GstStepInfo * current,gint64 rstart,gint64 rstop,gboolean eos)1955 stop_stepping (GstBaseSink * sink, GstSegment * segment,
1956 GstStepInfo * current, gint64 rstart, gint64 rstop, gboolean eos)
1957 {
1958 gint64 stop, position;
1959 GstMessage *message;
1960
1961 GST_DEBUG_OBJECT (sink, "step complete");
1962
1963 if (segment->rate > 0.0)
1964 stop = rstart;
1965 else
1966 stop = rstop;
1967
1968 GST_DEBUG_OBJECT (sink,
1969 "step stop at running_time %" GST_TIME_FORMAT, GST_TIME_ARGS (stop));
1970
1971 if (stop == -1)
1972 current->duration = current->position;
1973 else
1974 current->duration = stop - current->start;
1975
1976 GST_DEBUG_OBJECT (sink, "step elapsed running_time %" GST_TIME_FORMAT,
1977 GST_TIME_ARGS (current->duration));
1978
1979 position = current->start + current->duration;
1980
1981 /* now move the segment to the new running time */
1982 gst_segment_set_running_time (segment, GST_FORMAT_TIME, position);
1983
1984 if (current->flush) {
1985 /* and remove the time we flushed, start time did not change */
1986 segment->base = current->start;
1987 } else {
1988 /* start time is now the stepped position */
1989 gst_element_set_start_time (GST_ELEMENT_CAST (sink), position);
1990 }
1991
1992 /* restore the previous rate */
1993 segment->rate = current->start_rate;
1994
1995 if (segment->rate > 0.0)
1996 segment->stop = current->start_stop;
1997 else
1998 segment->start = current->start_start;
1999
2000 /* post the step done when we know the stepped duration in TIME */
2001 message =
2002 gst_message_new_step_done (GST_OBJECT_CAST (sink), current->format,
2003 current->amount, current->rate, current->flush, current->intermediate,
2004 current->duration, eos);
2005 gst_message_set_seqnum (message, current->seqnum);
2006 gst_element_post_message (GST_ELEMENT_CAST (sink), message);
2007
2008 if (!current->intermediate)
2009 sink->need_preroll = current->need_preroll;
2010
2011 /* and the current step info finished and becomes invalid */
2012 current->valid = FALSE;
2013 }
2014
2015 static gboolean
handle_stepping(GstBaseSink * sink,GstSegment * segment,GstStepInfo * current,guint64 * cstart,guint64 * cstop,guint64 * rstart,guint64 * rstop)2016 handle_stepping (GstBaseSink * sink, GstSegment * segment,
2017 GstStepInfo * current, guint64 * cstart, guint64 * cstop, guint64 * rstart,
2018 guint64 * rstop)
2019 {
2020 gboolean step_end = FALSE;
2021
2022 /* stepping never stops */
2023 if (current->amount == -1)
2024 return FALSE;
2025
2026 /* see if we need to skip this buffer because of stepping */
2027 switch (current->format) {
2028 case GST_FORMAT_TIME:
2029 {
2030 guint64 end;
2031 guint64 first, last;
2032 gdouble abs_rate;
2033
2034 if (segment->rate > 0.0) {
2035 if (segment->stop == *cstop)
2036 *rstop = *rstart + current->amount;
2037
2038 first = *rstart;
2039 last = *rstop;
2040 } else {
2041 if (segment->start == *cstart)
2042 *rstart = *rstop + current->amount;
2043
2044 first = *rstop;
2045 last = *rstart;
2046 }
2047
2048 end = current->start + current->amount;
2049 current->position = first - current->start;
2050
2051 abs_rate = ABS (segment->rate);
2052 if (G_UNLIKELY (abs_rate != 1.0))
2053 current->position /= abs_rate;
2054
2055 GST_DEBUG_OBJECT (sink,
2056 "buffer: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
2057 GST_TIME_ARGS (first), GST_TIME_ARGS (last));
2058 GST_DEBUG_OBJECT (sink,
2059 "got time step %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "/%"
2060 GST_TIME_FORMAT, GST_TIME_ARGS (current->position),
2061 GST_TIME_ARGS (last - current->start),
2062 GST_TIME_ARGS (current->amount));
2063
2064 if ((current->flush && current->position >= current->amount)
2065 || last >= end) {
2066 GST_DEBUG_OBJECT (sink, "step ended, we need clipping");
2067 step_end = TRUE;
2068 if (segment->rate > 0.0) {
2069 *rstart = end;
2070 *cstart =
2071 gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
2072 end);
2073 } else {
2074 *rstop = end;
2075 *cstop =
2076 gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
2077 end);
2078 }
2079 }
2080 GST_DEBUG_OBJECT (sink,
2081 "cstart %" GST_TIME_FORMAT ", rstart %" GST_TIME_FORMAT,
2082 GST_TIME_ARGS (*cstart), GST_TIME_ARGS (*rstart));
2083 GST_DEBUG_OBJECT (sink,
2084 "cstop %" GST_TIME_FORMAT ", rstop %" GST_TIME_FORMAT,
2085 GST_TIME_ARGS (*cstop), GST_TIME_ARGS (*rstop));
2086 break;
2087 }
2088 case GST_FORMAT_BUFFERS:
2089 GST_DEBUG_OBJECT (sink,
2090 "got default step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
2091 current->position, current->amount);
2092
2093 if (current->position < current->amount) {
2094 current->position++;
2095 } else {
2096 step_end = TRUE;
2097 }
2098 break;
2099 case GST_FORMAT_DEFAULT:
2100 default:
2101 GST_DEBUG_OBJECT (sink,
2102 "got unknown step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
2103 current->position, current->amount);
2104 break;
2105 }
2106 return step_end;
2107 }
2108
2109 /* with STREAM_LOCK, PREROLL_LOCK
2110 *
2111 * Returns %TRUE if the object needs synchronisation and takes therefore
2112 * part in prerolling.
2113 *
2114 * rsstart/rsstop contain the start/stop in stream time.
2115 * rrstart/rrstop contain the start/stop in running time.
2116 */
2117 static gboolean
gst_base_sink_get_sync_times(GstBaseSink * basesink,GstMiniObject * obj,GstClockTime * rsstart,GstClockTime * rsstop,GstClockTime * rrstart,GstClockTime * rrstop,GstClockTime * rrnext,gboolean * do_sync,gboolean * stepped,GstStepInfo * step,gboolean * step_end)2118 gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
2119 GstClockTime * rsstart, GstClockTime * rsstop,
2120 GstClockTime * rrstart, GstClockTime * rrstop, GstClockTime * rrnext,
2121 gboolean * do_sync, gboolean * stepped, GstStepInfo * step,
2122 gboolean * step_end)
2123 {
2124 GstBaseSinkClass *bclass;
2125 GstClockTime start, stop; /* raw start/stop timestamps */
2126 guint64 cstart, cstop; /* clipped raw timestamps */
2127 guint64 rstart, rstop, rnext; /* clipped timestamps converted to running time */
2128 GstClockTime sstart, sstop; /* clipped timestamps converted to stream time */
2129 GstFormat format;
2130 GstBaseSinkPrivate *priv;
2131 GstSegment *segment;
2132 gboolean eos;
2133
2134 priv = basesink->priv;
2135 segment = &basesink->segment;
2136
2137 bclass = GST_BASE_SINK_GET_CLASS (basesink);
2138
2139 again:
2140 /* start with nothing */
2141 start = stop = GST_CLOCK_TIME_NONE;
2142 eos = FALSE;
2143
2144 if (G_UNLIKELY (GST_IS_EVENT (obj))) {
2145 GstEvent *event = GST_EVENT_CAST (obj);
2146
2147 switch (GST_EVENT_TYPE (event)) {
2148 /* EOS event needs syncing */
2149 case GST_EVENT_EOS:
2150 {
2151 if (segment->rate >= 0.0) {
2152 sstart = sstop = priv->current_sstop;
2153 if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
2154 /* we have not seen a buffer yet, use the segment values */
2155 sstart = sstop = gst_segment_to_stream_time (segment,
2156 segment->format, segment->stop);
2157 }
2158 } else {
2159 sstart = sstop = priv->current_sstart;
2160 if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
2161 /* we have not seen a buffer yet, use the segment values */
2162 sstart = sstop = gst_segment_to_stream_time (segment,
2163 segment->format, segment->start);
2164 }
2165 }
2166
2167 rstart = rstop = rnext = priv->eos_rtime;
2168 *do_sync = GST_CLOCK_TIME_IS_VALID (rstart);
2169 GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
2170 GST_TIME_ARGS (rstart));
2171 /* if we are stepping, we end now */
2172 *step_end = step->valid;
2173 eos = TRUE;
2174 goto eos_done;
2175 }
2176 case GST_EVENT_GAP:
2177 {
2178 GstClockTime timestamp, duration;
2179 gst_event_parse_gap (event, ×tamp, &duration);
2180
2181 GST_DEBUG_OBJECT (basesink, "Got Gap time %" GST_TIME_FORMAT
2182 " duration %" GST_TIME_FORMAT,
2183 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
2184
2185 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
2186 start = timestamp;
2187 if (GST_CLOCK_TIME_IS_VALID (duration))
2188 stop = start + duration;
2189 }
2190 *do_sync = TRUE;
2191 break;
2192 }
2193 default:
2194 /* other events do not need syncing */
2195 return FALSE;
2196 }
2197 } else {
2198 /* else do buffer sync code */
2199 GstBuffer *buffer = GST_BUFFER_CAST (obj);
2200
2201 /* just get the times to see if we need syncing, if the retuned start is -1
2202 * we don't sync. */
2203 if (bclass->get_times)
2204 bclass->get_times (basesink, buffer, &start, &stop);
2205
2206 if (!GST_CLOCK_TIME_IS_VALID (start)) {
2207 /* we don't need to sync but we still want to get the timestamps for
2208 * tracking the position */
2209 gst_base_sink_default_get_times (basesink, buffer, &start, &stop);
2210 *do_sync = FALSE;
2211 } else {
2212 *do_sync = TRUE;
2213 }
2214 }
2215
2216 GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
2217 ", stop: %" GST_TIME_FORMAT ", do_sync %d", GST_TIME_ARGS (start),
2218 GST_TIME_ARGS (stop), *do_sync);
2219
2220 /* collect segment and format for code clarity */
2221 format = segment->format;
2222
2223 /* clip */
2224 if (G_UNLIKELY (!gst_segment_clip (segment, format,
2225 start, stop, &cstart, &cstop))) {
2226 if (step->valid) {
2227 GST_DEBUG_OBJECT (basesink, "step out of segment");
2228 /* when we are stepping, pretend we're at the end of the segment */
2229 if (segment->rate > 0.0) {
2230 cstart = segment->stop;
2231 cstop = segment->stop;
2232 } else {
2233 cstart = segment->start;
2234 cstop = segment->start;
2235 }
2236 goto do_times;
2237 }
2238 goto out_of_segment;
2239 }
2240
2241 if (G_UNLIKELY (start != cstart || stop != cstop)) {
2242 GST_DEBUG_OBJECT (basesink, "clipped to: start %" GST_TIME_FORMAT
2243 ", stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (cstart),
2244 GST_TIME_ARGS (cstop));
2245 }
2246
2247 /* set last stop position */
2248 if (G_LIKELY (stop != GST_CLOCK_TIME_NONE && cstop != GST_CLOCK_TIME_NONE))
2249 segment->position = cstop;
2250 else
2251 segment->position = cstart;
2252
2253 do_times:
2254 rstart = gst_segment_to_running_time (segment, format, cstart);
2255 rstop = gst_segment_to_running_time (segment, format, cstop);
2256
2257 /* In reverse playback, play from stop to start */
2258 if (segment->rate < 0.0 && GST_CLOCK_TIME_IS_VALID (rstop)
2259 /* FIXME: Current stepping implemenation expects unmodified rstart/rstop
2260 * for reverse playback. Don't swap those values when stepping
2261 * unless stepping code is updated as such */
2262 && !step->valid) {
2263 GstClockTime tmp = rstart;
2264 rstart = rstop;
2265 rstop = tmp;
2266 }
2267
2268 if (GST_CLOCK_TIME_IS_VALID (stop))
2269 rnext = rstop;
2270 else
2271 rnext = rstart;
2272
2273 if (G_UNLIKELY (step->valid)) {
2274 if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop,
2275 &rstart, &rstop))) {
2276 /* step is still busy, we discard data when we are flushing */
2277 *stepped = step->flush;
2278 GST_DEBUG_OBJECT (basesink, "stepping busy");
2279 }
2280 }
2281 /* this can produce wrong values if we accumulated non-TIME segments. If this happens,
2282 * upstream is behaving very badly */
2283 sstart = gst_segment_to_stream_time (segment, format, cstart);
2284 sstop = gst_segment_to_stream_time (segment, format, cstop);
2285
2286 eos_done:
2287 /* eos_done label only called when doing EOS, we also stop stepping then */
2288 if (*step_end && step->flush) {
2289 GST_DEBUG_OBJECT (basesink, "flushing step ended");
2290 stop_stepping (basesink, segment, step, rstart, rstop, eos);
2291 *step_end = FALSE;
2292 /* re-determine running start times for adjusted segment
2293 * (which has a flushed amount of running/accumulated time removed) */
2294 if (!GST_IS_EVENT (obj)) {
2295 GST_DEBUG_OBJECT (basesink, "refresh sync times");
2296 goto again;
2297 }
2298 }
2299
2300 /* save times */
2301 *rsstart = sstart;
2302 *rsstop = sstop;
2303 *rrstart = rstart;
2304 *rrstop = rstop;
2305 *rrnext = rnext;
2306
2307 /* buffers and EOS always need syncing and preroll */
2308 return TRUE;
2309
2310 /* special cases */
2311 out_of_segment:
2312 {
2313 /* we usually clip in the chain function already but stepping could cause
2314 * the segment to be updated later. we return %FALSE so that we don't try
2315 * to sync on it. */
2316 GST_LOG_OBJECT (basesink, "buffer skipped, not in segment");
2317 return FALSE;
2318 }
2319 }
2320
2321 /* with STREAM_LOCK, PREROLL_LOCK, LOCK
2322 * adjust a timestamp with the latency and timestamp offset. This function does
2323 * not adjust for the render delay. */
2324 static GstClockTime
gst_base_sink_adjust_time(GstBaseSink * basesink,GstClockTime time)2325 gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
2326 {
2327 GstClockTimeDiff ts_offset;
2328
2329 /* don't do anything funny with invalid timestamps */
2330 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time)))
2331 return time;
2332
2333 time += basesink->priv->latency;
2334
2335 /* apply offset, be careful for underflows */
2336 ts_offset = basesink->priv->ts_offset;
2337 if (ts_offset < 0) {
2338 ts_offset = -ts_offset;
2339 if (ts_offset < time)
2340 time -= ts_offset;
2341 else
2342 time = 0;
2343 } else
2344 time += ts_offset;
2345
2346 /* subtract the render delay again, which was included in the latency */
2347 if (time > basesink->priv->render_delay)
2348 time -= basesink->priv->render_delay;
2349 else
2350 time = 0;
2351
2352 return time;
2353 }
2354
2355 /**
2356 * gst_base_sink_wait_clock:
2357 * @sink: the sink
2358 * @time: the running_time to be reached
2359 * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
2360 *
2361 * This function will block until @time is reached. It is usually called by
2362 * subclasses that use their own internal synchronisation.
2363 *
2364 * If @time is not valid, no synchronisation is done and %GST_CLOCK_BADTIME is
2365 * returned. Likewise, if synchronisation is disabled in the element or there
2366 * is no clock, no synchronisation is done and %GST_CLOCK_BADTIME is returned.
2367 *
2368 * This function should only be called with the PREROLL_LOCK held, like when
2369 * receiving an EOS event in the #GstBaseSinkClass::event vmethod or when
2370 * receiving a buffer in
2371 * the #GstBaseSinkClass::render vmethod.
2372 *
2373 * The @time argument should be the running_time of when this method should
2374 * return and is not adjusted with any latency or offset configured in the
2375 * sink.
2376 *
2377 * Returns: #GstClockReturn
2378 */
2379 GstClockReturn
gst_base_sink_wait_clock(GstBaseSink * sink,GstClockTime time,GstClockTimeDiff * jitter)2380 gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
2381 GstClockTimeDiff * jitter)
2382 {
2383 GstClockReturn ret;
2384 GstClock *clock;
2385 GstClockTime base_time;
2386
2387 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time)))
2388 goto invalid_time;
2389
2390 GST_OBJECT_LOCK (sink);
2391 if (G_UNLIKELY (!sink->sync))
2392 goto no_sync;
2393
2394 if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL))
2395 goto no_clock;
2396
2397 base_time = GST_ELEMENT_CAST (sink)->base_time;
2398 GST_LOG_OBJECT (sink,
2399 "time %" GST_TIME_FORMAT ", base_time %" GST_TIME_FORMAT,
2400 GST_TIME_ARGS (time), GST_TIME_ARGS (base_time));
2401
2402 /* add base_time to running_time to get the time against the clock */
2403 time += base_time;
2404
2405 /* Re-use existing clockid if available */
2406 if (G_LIKELY (sink->priv->cached_clock_id != NULL
2407 && gst_clock_id_uses_clock (sink->priv->cached_clock_id, clock))) {
2408 if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
2409 time)) {
2410 gst_clock_id_unref (sink->priv->cached_clock_id);
2411 sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
2412 }
2413 } else {
2414 if (sink->priv->cached_clock_id != NULL)
2415 gst_clock_id_unref (sink->priv->cached_clock_id);
2416 sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
2417 }
2418 GST_OBJECT_UNLOCK (sink);
2419
2420 /* A blocking wait is performed on the clock. We save the ClockID
2421 * so we can unlock the entry at any time. While we are blocking, we
2422 * release the PREROLL_LOCK so that other threads can interrupt the
2423 * entry. */
2424 sink->clock_id = sink->priv->cached_clock_id;
2425 /* release the preroll lock while waiting */
2426 GST_BASE_SINK_PREROLL_UNLOCK (sink);
2427
2428 ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);
2429
2430 GST_BASE_SINK_PREROLL_LOCK (sink);
2431 sink->clock_id = NULL;
2432
2433 return ret;
2434
2435 /* no syncing needed */
2436 invalid_time:
2437 {
2438 GST_DEBUG_OBJECT (sink, "time not valid, no sync needed");
2439 return GST_CLOCK_BADTIME;
2440 }
2441 no_sync:
2442 {
2443 GST_DEBUG_OBJECT (sink, "sync disabled");
2444 GST_OBJECT_UNLOCK (sink);
2445 return GST_CLOCK_BADTIME;
2446 }
2447 no_clock:
2448 {
2449 GST_DEBUG_OBJECT (sink, "no clock, can't sync");
2450 GST_OBJECT_UNLOCK (sink);
2451 return GST_CLOCK_BADTIME;
2452 }
2453 }
2454
2455 /**
2456 * gst_base_sink_wait_preroll:
2457 * @sink: the sink
2458 *
2459 * If the #GstBaseSinkClass::render method performs its own synchronisation
2460 * against the clock it must unblock when going from PLAYING to the PAUSED state
2461 * and call this method before continuing to render the remaining data.
2462 *
2463 * If the #GstBaseSinkClass::render method can block on something else than
2464 * the clock, it must also be ready to unblock immediately on
2465 * the #GstBaseSinkClass::unlock method and cause the
2466 * #GstBaseSinkClass::render method to immediately call this function.
2467 * In this case, the subclass must be prepared to continue rendering where it
2468 * left off if this function returns %GST_FLOW_OK.
2469 *
2470 * This function will block until a state change to PLAYING happens (in which
2471 * case this function returns %GST_FLOW_OK) or the processing must be stopped due
2472 * to a state change to READY or a FLUSH event (in which case this function
2473 * returns %GST_FLOW_FLUSHING).
2474 *
2475 * This function should only be called with the PREROLL_LOCK held, like in the
2476 * render function.
2477 *
2478 * Returns: %GST_FLOW_OK if the preroll completed and processing can
2479 * continue. Any other return value should be returned from the render vmethod.
2480 */
2481 GstFlowReturn
gst_base_sink_wait_preroll(GstBaseSink * sink)2482 gst_base_sink_wait_preroll (GstBaseSink * sink)
2483 {
2484 /**
2485 * ohos.ext.func.0009
2486 * GST_BASE_SINK_PREROLL_WAIT is woken up when the system switches to playing,
2487 * but the system does not go down immediately.
2488 * In this case, if you switch to pause first, the error logic will be entered.
2489 * Now.If GST_BASE_SINK_PREROLL_WAIT does not go down during pause, continue to wait.
2490 */
2491 #ifdef OHOS_EXT_FUNC
2492 do {
2493 #endif
2494 sink->have_preroll = TRUE;
2495 GST_DEBUG_OBJECT (sink, "waiting in preroll for flush or PLAYING");
2496 /* block until the state changes, or we get a flush, or something */
2497 GST_BASE_SINK_PREROLL_WAIT (sink);
2498 sink->have_preroll = FALSE;
2499 if (G_UNLIKELY (sink->flushing))
2500 goto stopping;
2501 if (G_UNLIKELY (sink->priv->step_unlock))
2502 goto step_unlocked;
2503 GST_DEBUG_OBJECT (sink, "continue after preroll");
2504 // ohos.ext.func.0009
2505 #ifdef OHOS_EXT_FUNC
2506 } while ((GST_STATE(sink) == GST_STATE_PAUSED && GST_STATE_TARGET(sink) != GST_STATE_PLAYING)
2507 || GST_STATE_TARGET(sink) == GST_STATE_PAUSED);
2508 #endif
2509 return GST_FLOW_OK;
2510
2511 /* ERRORS */
2512 stopping:
2513 {
2514 GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush");
2515 return GST_FLOW_FLUSHING;
2516 }
2517 step_unlocked:
2518 {
2519 sink->priv->step_unlock = FALSE;
2520 GST_DEBUG_OBJECT (sink, "preroll interrupted because of step");
2521 return GST_FLOW_STEP;
2522 }
2523 }
2524
2525 /**
2526 * gst_base_sink_do_preroll:
2527 * @sink: the sink
2528 * @obj: (transfer none): the mini object that caused the preroll
2529 *
2530 * If the @sink spawns its own thread for pulling buffers from upstream it
2531 * should call this method after it has pulled a buffer. If the element needed
2532 * to preroll, this function will perform the preroll and will then block
2533 * until the element state is changed.
2534 *
2535 * This function should be called with the PREROLL_LOCK held.
2536 *
2537 * Returns: %GST_FLOW_OK if the preroll completed and processing can
2538 * continue. Any other return value should be returned from the render vmethod.
2539 */
2540 GstFlowReturn
gst_base_sink_do_preroll(GstBaseSink * sink,GstMiniObject * obj)2541 gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
2542 {
2543 GstFlowReturn ret;
2544
2545 while (G_UNLIKELY (sink->need_preroll)) {
2546 GST_DEBUG_OBJECT (sink, "prerolling object %p", obj);
2547
2548 /* if it's a buffer, we need to call the preroll method */
2549 if (sink->priv->call_preroll) {
2550 GstBaseSinkClass *bclass;
2551 GstBuffer *buf;
2552
2553 if (GST_IS_BUFFER_LIST (obj)) {
2554 buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
2555 gst_base_sink_set_last_buffer (sink, buf);
2556 gst_base_sink_set_last_buffer_list (sink, GST_BUFFER_LIST_CAST (obj));
2557 g_assert (NULL != buf);
2558 } else if (GST_IS_BUFFER (obj)) {
2559 buf = GST_BUFFER_CAST (obj);
2560 /* For buffer lists do not set last buffer for now */
2561 gst_base_sink_set_last_buffer (sink, buf);
2562 gst_base_sink_set_last_buffer_list (sink, NULL);
2563 } else {
2564 buf = NULL;
2565 }
2566
2567 if (buf) {
2568 GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT,
2569 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2570
2571 bclass = GST_BASE_SINK_GET_CLASS (sink);
2572
2573 if (bclass->prepare)
2574 if ((ret = bclass->prepare (sink, buf)) != GST_FLOW_OK)
2575 goto prepare_canceled;
2576
2577 if (bclass->preroll)
2578 if ((ret = bclass->preroll (sink, buf)) != GST_FLOW_OK)
2579 goto preroll_canceled;
2580
2581 sink->priv->call_preroll = FALSE;
2582 }
2583 }
2584
2585 /* commit state */
2586 if (G_LIKELY (sink->playing_async)) {
2587 if (G_UNLIKELY (!gst_base_sink_commit_state (sink)))
2588 goto stopping;
2589 }
2590
2591 /* need to recheck here because the commit state could have
2592 * made us not need the preroll anymore */
2593 if (G_LIKELY (sink->need_preroll)) {
2594 /* block until the state changes, or we get a flush, or something */
2595 ret = gst_base_sink_wait_preroll (sink);
2596 if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
2597 goto preroll_failed;
2598 }
2599 }
2600 return GST_FLOW_OK;
2601
2602 /* ERRORS */
2603 prepare_canceled:
2604 {
2605 GST_DEBUG_OBJECT (sink, "prepare failed, abort state");
2606 gst_element_abort_state (GST_ELEMENT_CAST (sink));
2607 return ret;
2608 }
2609 preroll_canceled:
2610 {
2611 GST_DEBUG_OBJECT (sink, "preroll failed, abort state");
2612 gst_element_abort_state (GST_ELEMENT_CAST (sink));
2613 return ret;
2614 }
2615 stopping:
2616 {
2617 GST_DEBUG_OBJECT (sink, "stopping while committing state");
2618 return GST_FLOW_FLUSHING;
2619 }
2620 preroll_failed:
2621 {
2622 GST_DEBUG_OBJECT (sink, "preroll failed: %s", gst_flow_get_name (ret));
2623 return ret;
2624 }
2625 }
2626
2627 /**
2628 * gst_base_sink_wait:
2629 * @sink: the sink
2630 * @time: the running_time to be reached
2631 * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
2632 *
2633 * This function will wait for preroll to complete and will then block until @time
2634 * is reached. It is usually called by subclasses that use their own internal
2635 * synchronisation but want to let some synchronization (like EOS) be handled
2636 * by the base class.
2637 *
2638 * This function should only be called with the PREROLL_LOCK held (like when
2639 * receiving an EOS event in the ::event vmethod or when handling buffers in
2640 * ::render).
2641 *
2642 * The @time argument should be the running_time of when the timeout should happen
2643 * and will be adjusted with any latency and offset configured in the sink.
2644 *
2645 * Returns: #GstFlowReturn
2646 */
2647 GstFlowReturn
gst_base_sink_wait(GstBaseSink * sink,GstClockTime time,GstClockTimeDiff * jitter)2648 gst_base_sink_wait (GstBaseSink * sink, GstClockTime time,
2649 GstClockTimeDiff * jitter)
2650 {
2651 GstClockReturn status;
2652 GstFlowReturn ret;
2653
2654 do {
2655 GstClockTime stime;
2656
2657 GST_DEBUG_OBJECT (sink, "checking preroll");
2658
2659 /* first wait for the playing state before we can continue */
2660 while (G_UNLIKELY (sink->need_preroll)) {
2661 ret = gst_base_sink_wait_preroll (sink);
2662 if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
2663 goto flushing;
2664 }
2665
2666 /* preroll done, we can sync since we are in PLAYING now. */
2667 GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
2668 GST_TIME_FORMAT, GST_TIME_ARGS (time));
2669
2670 /* compensate for latency, ts_offset and render delay */
2671 stime = gst_base_sink_adjust_time (sink, time);
2672
2673 /* wait for the clock, this can be interrupted because we got shut down or
2674 * we PAUSED. */
2675 status = gst_base_sink_wait_clock (sink, stime, jitter);
2676
2677 GST_DEBUG_OBJECT (sink, "clock returned %d", status);
2678
2679 /* invalid time, no clock or sync disabled, just continue then */
2680 if (status == GST_CLOCK_BADTIME)
2681 break;
2682
2683 /* waiting could have been interrupted and we can be flushing now */
2684 if (G_UNLIKELY (sink->flushing))
2685 goto flushing;
2686
2687 /* retry if we got unscheduled, which means we did not reach the timeout
2688 * yet. if some other error occurs, we continue. */
2689 } while (status == GST_CLOCK_UNSCHEDULED);
2690
2691 GST_DEBUG_OBJECT (sink, "end of stream");
2692
2693 return GST_FLOW_OK;
2694
2695 /* ERRORS */
2696 flushing:
2697 {
2698 GST_DEBUG_OBJECT (sink, "we are flushing");
2699 return GST_FLOW_FLUSHING;
2700 }
2701 }
2702
2703 /* with STREAM_LOCK, PREROLL_LOCK
2704 *
2705 * Make sure we are in PLAYING and synchronize an object to the clock.
2706 *
2707 * If we need preroll, we are not in PLAYING. We try to commit the state
2708 * if needed and then block if we still are not PLAYING.
2709 *
2710 * We start waiting on the clock in PLAYING. If we got interrupted, we
2711 * immediately try to re-preroll.
2712 *
2713 * Some objects do not need synchronisation (most events) and so this function
2714 * immediately returns GST_FLOW_OK.
2715 *
2716 * for objects that arrive later than max-lateness to be synchronized to the
2717 * clock have the @late boolean set to %TRUE.
2718 *
2719 * This function keeps a running average of the jitter (the diff between the
2720 * clock time and the requested sync time). The jitter is negative for
2721 * objects that arrive in time and positive for late buffers.
2722 *
2723 * does not take ownership of obj.
2724 */
2725 static GstFlowReturn
gst_base_sink_do_sync(GstBaseSink * basesink,GstMiniObject * obj,gboolean * late,gboolean * step_end)2726 gst_base_sink_do_sync (GstBaseSink * basesink,
2727 GstMiniObject * obj, gboolean * late, gboolean * step_end)
2728 {
2729 GstClockTimeDiff jitter = 0;
2730 gboolean syncable;
2731 GstClockReturn status = GST_CLOCK_OK;
2732 GstClockTime rstart, rstop, rnext, sstart, sstop, stime;
2733 gboolean do_sync;
2734 GstBaseSinkPrivate *priv;
2735 GstFlowReturn ret;
2736 GstStepInfo *current, *pending;
2737 gboolean stepped;
2738 guint32 current_instant_rate_seqnum;
2739
2740 priv = basesink->priv;
2741
2742 /* remember the currently handled instant-rate sequence number. If this
2743 * changes after pre-rolling, we need to goto do_step again for updating
2744 * the timing information of the current buffer */
2745 current_instant_rate_seqnum = priv->instant_rate_sync_seqnum;
2746 do_step:
2747 sstart = sstop = rstart = rstop = rnext = GST_CLOCK_TIME_NONE;
2748 do_sync = TRUE;
2749 stepped = FALSE;
2750
2751 priv->current_rstart = GST_CLOCK_TIME_NONE;
2752
2753 /* get stepping info */
2754 current = &priv->current_step;
2755 pending = &priv->pending_step;
2756
2757 /* get timing information for this object against the render segment */
2758 syncable = gst_base_sink_get_sync_times (basesink, obj,
2759 &sstart, &sstop, &rstart, &rstop, &rnext, &do_sync, &stepped, current,
2760 step_end);
2761
2762 if (G_UNLIKELY (stepped))
2763 goto step_skipped;
2764
2765 /* a syncable object needs to participate in preroll and
2766 * clocking. All buffers and EOS are syncable. */
2767 if (G_UNLIKELY (!syncable))
2768 goto not_syncable;
2769
2770 /* store timing info for current object */
2771 priv->current_rstart = rstart;
2772 priv->current_rstop = (GST_CLOCK_TIME_IS_VALID (rstop) ? rstop : rstart);
2773
2774 /* save sync time for eos when the previous object needed sync */
2775 priv->eos_rtime = (do_sync ? rnext : GST_CLOCK_TIME_NONE);
2776
2777 /* calculate inter frame spacing */
2778 if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->prev_rstart) &&
2779 priv->prev_rstart < rstart)) {
2780 GstClockTime in_diff;
2781
2782 in_diff = rstart - priv->prev_rstart;
2783
2784 if (priv->avg_in_diff == -1)
2785 priv->avg_in_diff = in_diff;
2786 else
2787 priv->avg_in_diff = UPDATE_RUNNING_AVG (priv->avg_in_diff, in_diff);
2788
2789 GST_LOG_OBJECT (basesink, "avg frame diff %" GST_TIME_FORMAT,
2790 GST_TIME_ARGS (priv->avg_in_diff));
2791
2792 }
2793 priv->prev_rstart = rstart;
2794
2795 if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->earliest_in_time) &&
2796 rstart < priv->earliest_in_time))
2797 goto qos_dropped;
2798
2799 again:
2800 /* first do preroll, this makes sure we commit our state
2801 * to PAUSED and can continue to PLAYING. We cannot perform
2802 * any clock sync in PAUSED because there is no clock. */
2803 ret = gst_base_sink_do_preroll (basesink, obj);
2804 if (G_UNLIKELY (ret != GST_FLOW_OK))
2805 goto preroll_failed;
2806
2807 /* update the segment with a pending step if the current one is invalid and we
2808 * have a new pending one. We only accept new step updates after a preroll */
2809 if (G_UNLIKELY (pending->valid && !current->valid)) {
2810 start_stepping (basesink, &basesink->segment, pending, current);
2811 goto do_step;
2812 }
2813
2814 if (G_UNLIKELY (priv->instant_rate_sync_seqnum !=
2815 current_instant_rate_seqnum)) {
2816 current_instant_rate_seqnum = priv->instant_rate_sync_seqnum;
2817 // TODO rename the goto label - it does more these days.
2818 goto do_step;
2819 }
2820
2821 /* After rendering we store the position of the last buffer so that we can use
2822 * it to report the position. We need to take the lock here. */
2823 GST_OBJECT_LOCK (basesink);
2824 priv->current_sstart = sstart;
2825 priv->current_sstop = (GST_CLOCK_TIME_IS_VALID (sstop) ? sstop : sstart);
2826 GST_OBJECT_UNLOCK (basesink);
2827
2828 if (!do_sync)
2829 goto done;
2830
2831 /* adjust for latency */
2832 stime = gst_base_sink_adjust_time (basesink, rstart);
2833
2834 /* adjust for rate control */
2835 if (priv->rc_next == -1 || (stime != -1 && stime >= priv->rc_next)) {
2836 GST_DEBUG_OBJECT (basesink, "reset rc_time to time %" GST_TIME_FORMAT,
2837 GST_TIME_ARGS (stime));
2838 priv->rc_time = stime;
2839 priv->rc_accumulated = 0;
2840 } else {
2841 GST_DEBUG_OBJECT (basesink, "rate control next %" GST_TIME_FORMAT,
2842 GST_TIME_ARGS (priv->rc_next));
2843 stime = priv->rc_next;
2844 }
2845
2846 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0004: drop buffer when it is too late
2847 gboolean need_drop_this_buffer = FALSE;
2848 #endif
2849
2850 /* #ifdef OHOS_OPT_PERFORMANCE: comment out this macro to avoid c file differences caused by macros
2851 * ohos.opt.performance.0002: update reach time for avsync */
2852 GstBaseSinkClass *bclass = GST_BASE_SINK_GET_CLASS (basesink);
2853 if (bclass != NULL && bclass->update_reach_time != NULL) {
2854 stime = bclass->update_reach_time (basesink, stime, &need_drop_this_buffer);
2855 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0004: drop buffer when it is too late
2856 if (G_UNLIKELY (need_drop_this_buffer && basesink->priv->received_eos != TRUE)) {
2857 goto dropped;
2858 }
2859 #endif
2860 }
2861 /* #endif */
2862
2863 #ifdef OHOS_EXT_FUNC // ohos.opt.performance.0006: delay Log
2864 /* preroll done, we can sync since we are in PLAYING now. */
2865 GST_INFO_OBJECT (basesink, "possibly waiting for clock to reach %"
2866 GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
2867 GST_TIME_ARGS (rstart), GST_TIME_ARGS (stime));
2868 #else
2869 /* preroll done, we can sync since we are in PLAYING now. */
2870 GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
2871 GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
2872 GST_TIME_ARGS (rstart), GST_TIME_ARGS (stime));
2873 #endif
2874
2875 /* This function will return immediately if start == -1, no clock
2876 * or sync is disabled with GST_CLOCK_BADTIME. */
2877 status = gst_base_sink_wait_clock (basesink, stime, &jitter);
2878
2879 GST_DEBUG_OBJECT (basesink, "clock returned %d, jitter %c%" GST_TIME_FORMAT,
2880 status, (jitter < 0 ? '-' : ' '), GST_TIME_ARGS (ABS (jitter)));
2881
2882 /* invalid time, no clock or sync disabled, just render */
2883 if (status == GST_CLOCK_BADTIME)
2884 goto done;
2885
2886 /* waiting could have been interrupted and we can be flushing now */
2887 if (G_UNLIKELY (basesink->flushing))
2888 goto flushing;
2889
2890 /* check for unlocked by a state change, we are not flushing so
2891 * we can try to preroll on the current buffer. */
2892 if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
2893 if (G_UNLIKELY (priv->instant_rate_sync_seqnum !=
2894 current_instant_rate_seqnum)) {
2895 current_instant_rate_seqnum = priv->instant_rate_sync_seqnum;
2896 // TODO rename
2897 goto do_step;
2898 }
2899
2900 GST_DEBUG_OBJECT (basesink, "unscheduled, waiting some more");
2901 priv->call_preroll = TRUE;
2902 goto again;
2903 }
2904
2905 /* successful syncing done, record observation */
2906 priv->current_jitter = jitter;
2907
2908 /* check if the object should be dropped */
2909 *late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
2910 status, jitter, TRUE);
2911
2912 done:
2913 return GST_FLOW_OK;
2914
2915 /* ERRORS */
2916 step_skipped:
2917 {
2918 GST_DEBUG_OBJECT (basesink, "skipped stepped object %p", obj);
2919 *late = TRUE;
2920 return GST_FLOW_OK;
2921 }
2922 not_syncable:
2923 {
2924 GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj);
2925 return GST_FLOW_OK;
2926 }
2927 qos_dropped:
2928 {
2929 GST_DEBUG_OBJECT (basesink, "dropped because of QoS %p", obj);
2930 *late = TRUE;
2931 return GST_FLOW_OK;
2932 }
2933 flushing:
2934 {
2935 GST_DEBUG_OBJECT (basesink, "we are flushing");
2936 return GST_FLOW_FLUSHING;
2937 }
2938 preroll_failed:
2939 {
2940 GST_DEBUG_OBJECT (basesink, "preroll failed");
2941 *step_end = FALSE;
2942 return ret;
2943 }
2944 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0004: drop buffer when it is too late
2945 dropped:
2946 {
2947 GST_DEBUG_OBJECT (basesink, "drop this buffer to do sync");
2948 return GST_FLOW_CUSTOM_ERROR;
2949 }
2950 #endif
2951 }
2952
2953 static gboolean
gst_base_sink_send_qos(GstBaseSink * basesink,GstQOSType type,gdouble proportion,GstClockTime time,GstClockTimeDiff diff)2954 gst_base_sink_send_qos (GstBaseSink * basesink, GstQOSType type,
2955 gdouble proportion, GstClockTime time, GstClockTimeDiff diff)
2956 {
2957 GstEvent *event;
2958 gboolean res;
2959
2960 /* generate Quality-of-Service event */
2961 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
2962 "qos: type %d, proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %"
2963 GST_TIME_FORMAT, type, proportion, diff, GST_TIME_ARGS (time));
2964
2965 /* Compensate for any instant-rate-change related running time offset
2966 * between upstream and the internal running time of the sink */
2967 if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) {
2968 GstClockTime actual_duration;
2969 GstClockTime upstream_duration;
2970 GstClockTimeDiff difference;
2971 gboolean negative_duration;
2972
2973 GST_DEBUG_OBJECT (basesink,
2974 "Current internal running time %" GST_TIME_FORMAT
2975 ", last internal running time %" GST_TIME_FORMAT, GST_TIME_ARGS (time),
2976 GST_TIME_ARGS (basesink->priv->last_anchor_running_time));
2977
2978 /* Calculate how much running time was spent since the last switch/segment
2979 * in the "corrected upstream segment", our segment */
2980 /* Due to rounding errors and other inaccuracies, it can happen
2981 * that our calculated internal running time is before the upstream
2982 * running time. We need to compensate for that */
2983 if (time < basesink->priv->last_anchor_running_time) {
2984 actual_duration = basesink->priv->last_anchor_running_time - time;
2985 negative_duration = TRUE;
2986 } else {
2987 actual_duration = time - basesink->priv->last_anchor_running_time;
2988 negative_duration = FALSE;
2989 }
2990
2991 /* Transpose that duration (i.e. what upstream beliefs) */
2992 upstream_duration =
2993 (actual_duration * basesink->segment.rate) /
2994 basesink->priv->upstream_segment.rate;
2995
2996 /* Add the difference to the previously accumulated correction */
2997 if (negative_duration)
2998 difference = upstream_duration - actual_duration;
2999 else
3000 difference = actual_duration - upstream_duration;
3001
3002 GST_DEBUG_OBJECT (basesink,
3003 "Current instant rate correction offset. Actual duration %"
3004 GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT
3005 ", negative %d, difference %" GST_STIME_FORMAT ", current offset %"
3006 GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration),
3007 GST_TIME_ARGS (upstream_duration), negative_duration,
3008 GST_STIME_ARGS (difference),
3009 GST_STIME_ARGS (basesink->priv->instant_rate_offset + difference));
3010
3011 difference = basesink->priv->instant_rate_offset + difference;
3012
3013 if (difference > 0 && time < difference)
3014 time = 0;
3015 else
3016 time -= difference;
3017 }
3018
3019 event = gst_event_new_qos (type, proportion, diff, time);
3020
3021 /* send upstream */
3022 res = gst_pad_push_event (basesink->sinkpad, event);
3023
3024 return res;
3025 }
3026
3027 static void
gst_base_sink_perform_qos(GstBaseSink * sink,gboolean dropped)3028 gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped)
3029 {
3030 GstBaseSinkPrivate *priv;
3031 GstClockTime start, stop;
3032 GstClockTimeDiff jitter;
3033 GstClockTime pt, entered, left;
3034 GstClockTime duration;
3035 gdouble rate;
3036
3037 priv = sink->priv;
3038
3039 start = priv->current_rstart;
3040
3041 if (priv->current_step.valid)
3042 return;
3043
3044 /* if Quality-of-Service disabled, do nothing */
3045 if (!g_atomic_int_get (&priv->qos_enabled) ||
3046 !GST_CLOCK_TIME_IS_VALID (start))
3047 return;
3048
3049 stop = priv->current_rstop;
3050 jitter = priv->current_jitter;
3051
3052 if (jitter < 0) {
3053 /* this is the time the buffer entered the sink */
3054 if (start < -jitter)
3055 entered = 0;
3056 else
3057 entered = start + jitter;
3058 left = start;
3059 } else {
3060 /* this is the time the buffer entered the sink */
3061 entered = start + jitter;
3062 /* this is the time the buffer left the sink */
3063 left = start + jitter;
3064 }
3065
3066 /* calculate duration of the buffer, only use buffer durations if not in
3067 * trick mode or key-unit mode. Otherwise the buffer durations will be
3068 * meaningless as frames are being dropped in-between without updating the
3069 * durations. */
3070 duration = priv->avg_in_diff;
3071
3072 /* if we have the time when the last buffer left us, calculate
3073 * processing time */
3074 if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
3075 if (entered > priv->last_left) {
3076 pt = entered - priv->last_left;
3077 } else {
3078 pt = 0;
3079 }
3080 } else {
3081 pt = priv->avg_pt;
3082 }
3083
3084 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "start: %" GST_TIME_FORMAT
3085 ", stop %" GST_TIME_FORMAT ", entered %" GST_TIME_FORMAT ", left %"
3086 GST_TIME_FORMAT ", pt: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
3087 ",jitter %" G_GINT64_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
3088 GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt),
3089 GST_TIME_ARGS (duration), jitter);
3090
3091 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
3092 "avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
3093 GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
3094
3095 /* collect running averages. for first observations, we copy the
3096 * values */
3097 if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt))
3098 priv->avg_pt = pt;
3099 else
3100 priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt);
3101
3102 if (duration != -1 && duration != 0) {
3103 rate =
3104 gst_guint64_to_gdouble (priv->avg_pt) /
3105 gst_guint64_to_gdouble (duration);
3106 } else {
3107 rate = 1.0;
3108 }
3109
3110 if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
3111 if (dropped || priv->avg_rate < 0.0) {
3112 priv->avg_rate = rate;
3113 } else {
3114 if (rate > 1.0)
3115 priv->avg_rate = UPDATE_RUNNING_AVG_N (priv->avg_rate, rate);
3116 else
3117 priv->avg_rate = UPDATE_RUNNING_AVG_P (priv->avg_rate, rate);
3118 }
3119 }
3120
3121 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
3122 "updated: avg_pt: %" GST_TIME_FORMAT
3123 ", avg_rate: %g", GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
3124
3125
3126 if (priv->avg_rate >= 0.0) {
3127 GstQOSType type;
3128 GstClockTimeDiff diff;
3129
3130 /* if we have a valid rate, start sending QoS messages */
3131 if (priv->current_jitter < 0) {
3132 /* make sure we never go below 0 when adding the jitter to the
3133 * timestamp. */
3134 if (priv->current_rstart < -priv->current_jitter)
3135 priv->current_jitter = -priv->current_rstart;
3136 }
3137
3138 if (priv->throttle_time > 0) {
3139 diff = priv->throttle_time;
3140 type = GST_QOS_TYPE_THROTTLE;
3141 } else {
3142 diff = priv->current_jitter;
3143 if (diff <= 0)
3144 type = GST_QOS_TYPE_OVERFLOW;
3145 else
3146 type = GST_QOS_TYPE_UNDERFLOW;
3147 }
3148
3149 gst_base_sink_send_qos (sink, type, priv->avg_rate, priv->current_rstart,
3150 diff);
3151 }
3152
3153 /* record when this buffer will leave us */
3154 priv->last_left = left;
3155 }
3156
3157 /* reset all qos measuring */
3158 static void
gst_base_sink_reset_qos(GstBaseSink * sink)3159 gst_base_sink_reset_qos (GstBaseSink * sink)
3160 {
3161 GstBaseSinkPrivate *priv;
3162
3163 priv = sink->priv;
3164
3165 priv->last_render_time = GST_CLOCK_TIME_NONE;
3166 priv->prev_rstart = GST_CLOCK_TIME_NONE;
3167 priv->earliest_in_time = GST_CLOCK_TIME_NONE;
3168 priv->last_left = GST_CLOCK_TIME_NONE;
3169 priv->avg_pt = GST_CLOCK_TIME_NONE;
3170 priv->avg_rate = -1.0;
3171 priv->avg_in_diff = GST_CLOCK_TIME_NONE;
3172 priv->rendered = 0;
3173 priv->dropped = 0;
3174
3175 }
3176
3177 /* Checks if the object was scheduled too late.
3178 *
3179 * rstart/rstop contain the running_time start and stop values
3180 * of the object.
3181 *
3182 * status and jitter contain the return values from the clock wait.
3183 *
3184 * returns %TRUE if the buffer was too late.
3185 */
3186 static gboolean
gst_base_sink_is_too_late(GstBaseSink * basesink,GstMiniObject * obj,GstClockTime rstart,GstClockTime rstop,GstClockReturn status,GstClockTimeDiff jitter,gboolean render)3187 gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
3188 GstClockTime rstart, GstClockTime rstop,
3189 GstClockReturn status, GstClockTimeDiff jitter, gboolean render)
3190 {
3191 gboolean late;
3192 guint64 max_lateness;
3193 GstBaseSinkPrivate *priv;
3194
3195 priv = basesink->priv;
3196
3197 late = FALSE;
3198
3199 /* only for objects that were too late */
3200 if (G_LIKELY (status != GST_CLOCK_EARLY))
3201 goto in_time;
3202
3203 max_lateness = basesink->max_lateness;
3204
3205 /* check if frame dropping is enabled */
3206 if (max_lateness == -1)
3207 goto no_drop;
3208
3209 /* only check for buffers */
3210 if (G_UNLIKELY (!GST_IS_BUFFER (obj)))
3211 goto not_buffer;
3212
3213 /* can't do check if we don't have a timestamp */
3214 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rstart)))
3215 goto no_timestamp;
3216
3217 /* we can add a valid stop time */
3218 if (GST_CLOCK_TIME_IS_VALID (rstop))
3219 max_lateness += rstop;
3220 else {
3221 max_lateness += rstart;
3222 /* no stop time, use avg frame diff */
3223 if (priv->avg_in_diff != -1)
3224 max_lateness += priv->avg_in_diff;
3225 }
3226
3227 /* if the jitter bigger than duration and lateness we are too late */
3228 if ((late = rstart + jitter > max_lateness)) {
3229 GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
3230 "buffer is too late %" GST_TIME_FORMAT
3231 " > %" GST_TIME_FORMAT, GST_TIME_ARGS (rstart + jitter),
3232 GST_TIME_ARGS (max_lateness));
3233 /* !!emergency!!, if we did not receive anything valid for more than a
3234 * second, render it anyway so the user sees something */
3235 if (GST_CLOCK_TIME_IS_VALID (priv->last_render_time) &&
3236 rstart - priv->last_render_time > GST_SECOND) {
3237 late = FALSE;
3238 GST_ELEMENT_WARNING (basesink, CORE, CLOCK,
3239 (_("A lot of buffers are being dropped.")),
3240 ("There may be a timestamping problem, or this computer is too slow."));
3241 GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
3242 "**emergency** last buffer at %" GST_TIME_FORMAT " > GST_SECOND",
3243 GST_TIME_ARGS (priv->last_render_time));
3244 }
3245 }
3246
3247 done:
3248 if (render && (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time))) {
3249 priv->last_render_time = rstart;
3250 /* the next allowed input timestamp */
3251 if (priv->throttle_time > 0)
3252 priv->earliest_in_time = rstart + priv->throttle_time;
3253 }
3254 return late;
3255
3256 /* all is fine */
3257 in_time:
3258 {
3259 GST_DEBUG_OBJECT (basesink, "object was scheduled in time");
3260 goto done;
3261 }
3262 no_drop:
3263 {
3264 GST_DEBUG_OBJECT (basesink, "frame dropping disabled");
3265 goto done;
3266 }
3267 not_buffer:
3268 {
3269 GST_DEBUG_OBJECT (basesink, "object is not a buffer");
3270 return FALSE;
3271 }
3272 no_timestamp:
3273 {
3274 GST_DEBUG_OBJECT (basesink, "buffer has no timestamp");
3275 return FALSE;
3276 }
3277 }
3278
3279 static void
gst_base_sink_update_start_time(GstBaseSink * basesink)3280 gst_base_sink_update_start_time (GstBaseSink * basesink)
3281 {
3282 GstClock *clock;
3283
3284 GST_OBJECT_LOCK (basesink);
3285 if (GST_STATE (basesink) == GST_STATE_PLAYING
3286 && (clock = GST_ELEMENT_CLOCK (basesink))) {
3287 GstClockTime now;
3288
3289 gst_object_ref (clock);
3290 GST_OBJECT_UNLOCK (basesink);
3291
3292 /* calculate the time when we stopped */
3293 now = gst_clock_get_time (clock);
3294 gst_object_unref (clock);
3295
3296 GST_OBJECT_LOCK (basesink);
3297 /* store the current running time */
3298 if (GST_ELEMENT_START_TIME (basesink) != GST_CLOCK_TIME_NONE) {
3299 if (now != GST_CLOCK_TIME_NONE)
3300 GST_ELEMENT_START_TIME (basesink) =
3301 now - GST_ELEMENT_CAST (basesink)->base_time;
3302 else
3303 GST_WARNING_OBJECT (basesink,
3304 "Clock %s returned invalid time, can't calculate "
3305 "running_time when going to the PAUSED state",
3306 GST_OBJECT_NAME (clock));
3307 }
3308 GST_DEBUG_OBJECT (basesink,
3309 "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
3310 ", base_time %" GST_TIME_FORMAT,
3311 GST_TIME_ARGS (GST_ELEMENT_START_TIME (basesink)),
3312 GST_TIME_ARGS (now),
3313 GST_TIME_ARGS (GST_ELEMENT_CAST (basesink)->base_time));
3314 }
3315 GST_OBJECT_UNLOCK (basesink);
3316 }
3317
3318 static void
gst_base_sink_flush_start(GstBaseSink * basesink,GstPad * pad)3319 gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
3320 {
3321 /* make sure we are not blocked on the clock also clear any pending
3322 * eos state. */
3323 gst_base_sink_set_flushing (basesink, pad, TRUE);
3324
3325 /* we grab the stream lock but that is not needed since setting the
3326 * sink to flushing would make sure no state commit is being done
3327 * anymore */
3328 GST_PAD_STREAM_LOCK (pad);
3329 gst_base_sink_reset_qos (basesink);
3330 /* and we need to commit our state again on the next
3331 * prerolled buffer */
3332 basesink->playing_async = TRUE;
3333 if (basesink->priv->async_enabled) {
3334 gst_base_sink_update_start_time (basesink);
3335 gst_element_lost_state (GST_ELEMENT_CAST (basesink));
3336 } else {
3337 /* start time reset in above case as well;
3338 * arranges for a.o. proper position reporting when flushing in PAUSED */
3339 gst_element_set_start_time (GST_ELEMENT_CAST (basesink), 0);
3340 basesink->priv->have_latency = TRUE;
3341 }
3342 gst_base_sink_set_last_buffer (basesink, NULL);
3343 gst_base_sink_set_last_buffer_list (basesink, NULL);
3344 GST_PAD_STREAM_UNLOCK (pad);
3345 }
3346
3347 static void
gst_base_sink_flush_stop(GstBaseSink * basesink,GstPad * pad,gboolean reset_time)3348 gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad,
3349 gboolean reset_time)
3350 {
3351 /* unset flushing so we can accept new data, this also flushes out any EOS
3352 * event. */
3353 gst_base_sink_set_flushing (basesink, pad, FALSE);
3354
3355 /* for position reporting */
3356 GST_OBJECT_LOCK (basesink);
3357 basesink->priv->current_sstart = GST_CLOCK_TIME_NONE;
3358 basesink->priv->current_sstop = GST_CLOCK_TIME_NONE;
3359 basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE;
3360 basesink->priv->call_preroll = TRUE;
3361 basesink->priv->current_step.valid = FALSE;
3362 basesink->priv->pending_step.valid = FALSE;
3363 if (basesink->pad_mode == GST_PAD_MODE_PUSH) {
3364 /* we need new segment info after the flush. */
3365 basesink->have_newsegment = FALSE;
3366 if (reset_time) {
3367 gst_segment_init (&basesink->priv->upstream_segment,
3368 GST_FORMAT_UNDEFINED);
3369 gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
3370 GST_ELEMENT_START_TIME (basesink) = 0;
3371
3372 basesink->priv->last_instant_rate_seqnum = GST_SEQNUM_INVALID;
3373 basesink->priv->instant_rate_sync_seqnum = GST_SEQNUM_INVALID;
3374 basesink->priv->instant_rate_multiplier = 0;
3375 basesink->priv->segment_seqnum = GST_SEQNUM_INVALID;
3376 basesink->priv->instant_rate_offset = 0;
3377 basesink->priv->last_anchor_running_time = 0;
3378 }
3379 }
3380 GST_OBJECT_UNLOCK (basesink);
3381
3382 if (reset_time) {
3383 GST_DEBUG_OBJECT (basesink, "posting reset-time message");
3384 gst_element_post_message (GST_ELEMENT_CAST (basesink),
3385 gst_message_new_reset_time (GST_OBJECT_CAST (basesink), 0));
3386 }
3387 }
3388
3389 static GstFlowReturn
gst_base_sink_default_wait_event(GstBaseSink * basesink,GstEvent * event)3390 gst_base_sink_default_wait_event (GstBaseSink * basesink, GstEvent * event)
3391 {
3392 GstFlowReturn ret;
3393 gboolean late, step_end = FALSE;
3394
3395 ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
3396 &late, &step_end);
3397
3398 return ret;
3399 }
3400
3401 static GstFlowReturn
gst_base_sink_wait_event(GstBaseSink * basesink,GstEvent * event)3402 gst_base_sink_wait_event (GstBaseSink * basesink, GstEvent * event)
3403 {
3404 GstFlowReturn ret;
3405 GstBaseSinkClass *bclass;
3406
3407 bclass = GST_BASE_SINK_GET_CLASS (basesink);
3408
3409 if (G_LIKELY (bclass->wait_event))
3410 ret = bclass->wait_event (basesink, event);
3411 else
3412 ret = GST_FLOW_NOT_SUPPORTED;
3413
3414 return ret;
3415 }
3416
3417 static gboolean
gst_base_sink_default_event(GstBaseSink * basesink,GstEvent * event)3418 gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
3419 {
3420 gboolean result = TRUE;
3421 GstBaseSinkClass *bclass;
3422
3423 bclass = GST_BASE_SINK_GET_CLASS (basesink);
3424
3425 switch (GST_EVENT_TYPE (event)) {
3426 case GST_EVENT_FLUSH_START:
3427 {
3428 GST_DEBUG_OBJECT (basesink, "flush-start %p", event);
3429 gst_base_sink_flush_start (basesink, basesink->sinkpad);
3430 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0007: First frame after basesink refresh
3431 GST_OBJECT_LOCK(basesink);
3432 GstBaseSinkPrivate *priv = basesink->priv;
3433 if (priv) {
3434 priv->has_render_first_frame = FALSE;
3435 priv->has_recv_first_frame = FALSE;
3436 }
3437 GST_OBJECT_UNLOCK(basesink);
3438 #endif
3439 break;
3440 }
3441 case GST_EVENT_FLUSH_STOP:
3442 {
3443 gboolean reset_time;
3444
3445 gst_event_parse_flush_stop (event, &reset_time);
3446 GST_DEBUG_OBJECT (basesink, "flush-stop %p, reset_time: %d", event,
3447 reset_time);
3448 gst_base_sink_flush_stop (basesink, basesink->sinkpad, reset_time);
3449 break;
3450 }
3451 case GST_EVENT_EOS:
3452 {
3453 GstMessage *message;
3454 guint32 seqnum;
3455
3456 /* we set the received EOS flag here so that we can use it when testing if
3457 * we are prerolled and to refuse more buffers. */
3458 basesink->priv->received_eos = TRUE;
3459
3460 /* wait for EOS */
3461 if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
3462 event) != GST_FLOW_OK)) {
3463 result = FALSE;
3464 goto done;
3465 }
3466
3467 /* the EOS event is completely handled so we mark
3468 * ourselves as being in the EOS state. eos is also
3469 * protected by the object lock so we can read it when
3470 * answering the POSITION query. */
3471 GST_OBJECT_LOCK (basesink);
3472 basesink->eos = TRUE;
3473 GST_OBJECT_UNLOCK (basesink);
3474
3475 /* ok, now we can post the message */
3476 GST_DEBUG_OBJECT (basesink, "Now posting EOS");
3477
3478 seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event);
3479 GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum);
3480
3481 message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
3482 gst_message_set_seqnum (message, seqnum);
3483 gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
3484 break;
3485 }
3486 case GST_EVENT_STREAM_START:
3487 {
3488 GstMessage *message;
3489 guint32 seqnum;
3490 guint group_id;
3491
3492 seqnum = gst_event_get_seqnum (event);
3493 GST_DEBUG_OBJECT (basesink, "Now posting STREAM_START (seqnum:%d)",
3494 seqnum);
3495 message = gst_message_new_stream_start (GST_OBJECT_CAST (basesink));
3496 if (gst_event_parse_group_id (event, &group_id)) {
3497 gst_message_set_group_id (message, group_id);
3498 } else {
3499 GST_FIXME_OBJECT (basesink, "stream-start event without group-id. "
3500 "Consider implementing group-id handling in the upstream "
3501 "elements");
3502 }
3503 gst_message_set_seqnum (message, seqnum);
3504 gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
3505 break;
3506 }
3507 case GST_EVENT_CAPS:
3508 {
3509 GstCaps *caps, *current_caps;
3510
3511 GST_DEBUG_OBJECT (basesink, "caps %p", event);
3512
3513 gst_event_parse_caps (event, &caps);
3514 current_caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (basesink));
3515
3516 if (current_caps && gst_caps_is_equal (current_caps, caps)) {
3517 GST_DEBUG_OBJECT (basesink,
3518 "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
3519 } else {
3520 if (bclass->set_caps)
3521 result = bclass->set_caps (basesink, caps);
3522
3523 if (result) {
3524 GST_OBJECT_LOCK (basesink);
3525 gst_caps_replace (&basesink->priv->caps, caps);
3526 GST_OBJECT_UNLOCK (basesink);
3527 }
3528 }
3529 if (current_caps)
3530 gst_caps_unref (current_caps);
3531 break;
3532 }
3533 case GST_EVENT_SEGMENT:{
3534 guint32 seqnum = gst_event_get_seqnum (event);
3535 GstSegment new_segment;
3536
3537 /* configure the segment */
3538 /* The segment is protected with both the STREAM_LOCK and the OBJECT_LOCK.
3539 * We protect with the OBJECT_LOCK so that we can use the values to
3540 * safely answer a POSITION query. */
3541 GST_OBJECT_LOCK (basesink);
3542 /* the new segment event is needed to bring the buffer timestamps to the
3543 * stream time and to drop samples outside of the playback segment. */
3544
3545 gst_event_copy_segment (event, &new_segment);
3546
3547 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0006: delay Log
3548 GST_INFO_OBJECT (basesink,
3549 "received upstream segment %u %" GST_SEGMENT_FORMAT, seqnum,
3550 &new_segment);
3551 // the pts segment of the first frame is calibrated to improve the performance
3552 if (new_segment.flags & GST_SEGMENT_FLAG_FIRST_FRAME) {
3553 if (basesink->priv->has_recv_first_frame) {
3554 GST_WARNING_OBJECT (basesink, "invalid segment, later than recv first frame");
3555 GST_OBJECT_UNLOCK(basesink);
3556 break;
3557 } else {
3558 GST_INFO_OBJECT (basesink, "received first frame segment");
3559 }
3560 }
3561 #else
3562 GST_DEBUG_OBJECT (basesink,
3563 "received upstream segment %u %" GST_SEGMENT_FORMAT, seqnum,
3564 &new_segment);
3565 #endif
3566
3567 /* Make sure that the position stays between start and stop */
3568 new_segment.position =
3569 CLAMP (new_segment.position, new_segment.start, new_segment.stop);
3570
3571 if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) {
3572 GstClockTime upstream_duration;
3573 GstClockTime actual_duration;
3574 GstClockTime new_segment_running_time;
3575 GstClockTimeDiff difference;
3576 gboolean negative_duration;
3577
3578 /* Calculate how much running time upstream believes has passed since
3579 * the last switch/segment */
3580 new_segment_running_time =
3581 gst_segment_to_running_time (&new_segment, GST_FORMAT_TIME,
3582 new_segment.position);
3583
3584 GST_DEBUG_OBJECT (basesink,
3585 "Current upstream running time %" GST_TIME_FORMAT
3586 ", last upstream running time %" GST_TIME_FORMAT,
3587 GST_TIME_ARGS (new_segment_running_time),
3588 GST_TIME_ARGS (basesink->priv->last_anchor_running_time -
3589 basesink->priv->instant_rate_offset));
3590
3591 /* Due to rounding errors and other inaccuracies, it can happen
3592 * that our calculated internal running time is before the upstream
3593 * running time. We need to compensate for that */
3594 if (new_segment_running_time <
3595 basesink->priv->last_anchor_running_time -
3596 basesink->priv->instant_rate_offset) {
3597 upstream_duration =
3598 basesink->priv->last_anchor_running_time -
3599 basesink->priv->instant_rate_offset - new_segment_running_time;
3600 negative_duration = TRUE;
3601 } else {
3602 upstream_duration =
3603 new_segment_running_time -
3604 basesink->priv->last_anchor_running_time +
3605 basesink->priv->instant_rate_offset;
3606 negative_duration = FALSE;
3607 }
3608
3609 /* Calculate the actual running-time duration of the previous segment */
3610 actual_duration =
3611 (upstream_duration * basesink->priv->instant_rate_multiplier);
3612
3613 if (negative_duration)
3614 difference = upstream_duration - actual_duration;
3615 else
3616 difference = actual_duration - upstream_duration;
3617
3618 GST_DEBUG_OBJECT (basesink,
3619 "Current internal running time %" GST_TIME_FORMAT
3620 ", last internal running time %" GST_TIME_FORMAT,
3621 GST_TIME_ARGS (new_segment_running_time +
3622 basesink->priv->instant_rate_offset + difference),
3623 GST_TIME_ARGS (basesink->priv->last_anchor_running_time));
3624
3625 /* Add the difference to the previously accumulated correction. */
3626 basesink->priv->instant_rate_offset += difference;
3627
3628 GST_DEBUG_OBJECT (basesink,
3629 "Updating instant rate correction offset. Actual duration %"
3630 GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT
3631 ", negative %d, difference %" GST_STIME_FORMAT ", new offset %"
3632 GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration),
3633 GST_TIME_ARGS (upstream_duration),
3634 negative_duration,
3635 GST_STIME_ARGS (difference),
3636 GST_STIME_ARGS (basesink->priv->instant_rate_offset));
3637
3638 if (basesink->priv->instant_rate_offset < 0 &&
3639 new_segment_running_time < -basesink->priv->instant_rate_offset) {
3640 GST_WARNING_OBJECT (basesink,
3641 "Upstream current running time %" GST_TIME_FORMAT
3642 " is smaller than calculated offset %" GST_STIME_FORMAT,
3643 GST_TIME_ARGS (new_segment_running_time),
3644 GST_STIME_ARGS (basesink->priv->instant_rate_offset));
3645
3646 basesink->priv->last_anchor_running_time = 0;
3647 basesink->priv->instant_rate_offset = 0;
3648 } else {
3649 basesink->priv->last_anchor_running_time =
3650 new_segment_running_time + basesink->priv->instant_rate_offset;
3651 }
3652
3653 /* Update the segments from the event and with the newly calculated
3654 * correction offset */
3655 basesink->priv->upstream_segment = new_segment;
3656 basesink->segment = new_segment;
3657
3658 basesink->segment.rate *= basesink->priv->instant_rate_multiplier;
3659
3660 gst_segment_offset_running_time (&basesink->segment, GST_FORMAT_TIME,
3661 basesink->priv->instant_rate_offset);
3662
3663 GST_DEBUG_OBJECT (basesink,
3664 "Adjusted segment is now %" GST_SEGMENT_FORMAT, &basesink->segment);
3665 } else {
3666 /* otherwise both segments are simply the same, no correction needed */
3667 basesink->priv->upstream_segment = new_segment;
3668 basesink->segment = new_segment;
3669 basesink->priv->last_anchor_running_time =
3670 gst_segment_to_running_time (&new_segment, new_segment.format,
3671 new_segment.position);
3672 basesink->priv->instant_rate_offset = 0; /* Should already be 0, but to be sure */
3673 }
3674
3675 GST_DEBUG_OBJECT (basesink, "configured segment %" GST_SEGMENT_FORMAT,
3676 &basesink->segment);
3677 basesink->priv->segment_seqnum = seqnum;
3678 basesink->have_newsegment = TRUE;
3679 gst_base_sink_reset_qos (basesink);
3680 GST_OBJECT_UNLOCK (basesink);
3681 break;
3682 }
3683 case GST_EVENT_GAP:
3684 {
3685 if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
3686 event) != GST_FLOW_OK))
3687 result = FALSE;
3688 break;
3689 }
3690 case GST_EVENT_TAG:
3691 {
3692 GstTagList *taglist;
3693
3694 gst_event_parse_tag (event, &taglist);
3695
3696 gst_element_post_message (GST_ELEMENT_CAST (basesink),
3697 gst_message_new_tag (GST_OBJECT_CAST (basesink),
3698 gst_tag_list_copy (taglist)));
3699 break;
3700 }
3701 case GST_EVENT_TOC:
3702 {
3703 GstToc *toc;
3704 gboolean updated;
3705
3706 gst_event_parse_toc (event, &toc, &updated);
3707
3708 gst_element_post_message (GST_ELEMENT_CAST (basesink),
3709 gst_message_new_toc (GST_OBJECT_CAST (basesink), toc, updated));
3710
3711 gst_toc_unref (toc);
3712 break;
3713 }
3714 case GST_EVENT_SINK_MESSAGE:
3715 {
3716 GstMessage *msg = NULL;
3717
3718 gst_event_parse_sink_message (event, &msg);
3719 if (msg)
3720 gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
3721 break;
3722 }
3723 case GST_EVENT_INSTANT_RATE_CHANGE:
3724 {
3725 GstMessage *msg;
3726 gdouble rate_multiplier;
3727 guint32 seqnum = gst_event_get_seqnum (event);
3728
3729 GST_OBJECT_LOCK (basesink);
3730 if (G_UNLIKELY (basesink->priv->last_instant_rate_seqnum == seqnum)) {
3731 /* Ignore repeated event */
3732 GST_LOG_OBJECT (basesink,
3733 "Ignoring repeated instant-rate-change event");
3734 GST_OBJECT_UNLOCK (basesink);
3735 break;
3736 }
3737 if (basesink->priv->instant_rate_sync_seqnum == seqnum) {
3738 /* Ignore if we already received the instant-rate-sync-time event from the pipeline */
3739 GST_LOG_OBJECT (basesink,
3740 "Ignoring instant-rate-change event for which we already received instant-rate-sync-time");
3741 GST_OBJECT_UNLOCK (basesink);
3742 break;
3743 }
3744
3745 basesink->priv->last_instant_rate_seqnum = seqnum;
3746 GST_OBJECT_UNLOCK (basesink);
3747
3748 gst_event_parse_instant_rate_change (event, &rate_multiplier, NULL);
3749
3750 msg =
3751 gst_message_new_instant_rate_request (GST_OBJECT_CAST (basesink),
3752 rate_multiplier);
3753 gst_message_set_seqnum (msg, seqnum);
3754 gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
3755
3756 break;
3757 }
3758 default:
3759 break;
3760 }
3761 done:
3762 gst_event_unref (event);
3763
3764 return result;
3765 }
3766
3767 static gboolean
gst_base_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)3768 gst_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
3769 {
3770 GstBaseSink *basesink;
3771 gboolean result = TRUE;
3772 GstBaseSinkClass *bclass;
3773
3774 basesink = GST_BASE_SINK_CAST (parent);
3775 bclass = GST_BASE_SINK_GET_CLASS (basesink);
3776
3777 GST_DEBUG_OBJECT (basesink, "received event %p %" GST_PTR_FORMAT, event,
3778 event);
3779
3780 switch (GST_EVENT_TYPE (event)) {
3781 case GST_EVENT_FLUSH_STOP:
3782 /* special case for this serialized event because we don't want to grab
3783 * the PREROLL lock or check if we were flushing */
3784 if (bclass->event)
3785 result = bclass->event (basesink, event);
3786 break;
3787 default:
3788 if (GST_EVENT_IS_SERIALIZED (event)) {
3789 GST_BASE_SINK_PREROLL_LOCK (basesink);
3790 if (G_UNLIKELY (basesink->flushing))
3791 goto flushing;
3792
3793 if (G_UNLIKELY (basesink->priv->received_eos))
3794 goto after_eos;
3795
3796 if (bclass->event)
3797 result = bclass->event (basesink, event);
3798
3799 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
3800 } else {
3801 if (bclass->event)
3802 result = bclass->event (basesink, event);
3803 }
3804 break;
3805 }
3806 done:
3807 return result;
3808
3809 /* ERRORS */
3810 flushing:
3811 {
3812 GST_DEBUG_OBJECT (basesink, "we are flushing");
3813 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
3814 gst_event_unref (event);
3815 result = FALSE;
3816 goto done;
3817 }
3818
3819 after_eos:
3820 {
3821 GST_DEBUG_OBJECT (basesink, "Event received after EOS, dropping");
3822 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
3823 gst_event_unref (event);
3824 result = FALSE;
3825 goto done;
3826 }
3827 }
3828
3829 /* default implementation to calculate the start and end
3830 * timestamps on a buffer, subclasses can override
3831 */
3832 static void
gst_base_sink_default_get_times(GstBaseSink * basesink,GstBuffer * buffer,GstClockTime * start,GstClockTime * end)3833 gst_base_sink_default_get_times (GstBaseSink * basesink, GstBuffer * buffer,
3834 GstClockTime * start, GstClockTime * end)
3835 {
3836 GstClockTime timestamp, duration;
3837
3838 /* first sync on DTS, else use PTS */
3839 timestamp = GST_BUFFER_DTS (buffer);
3840 if (!GST_CLOCK_TIME_IS_VALID (timestamp))
3841 timestamp = GST_BUFFER_PTS (buffer);
3842
3843 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
3844 /* get duration to calculate end time */
3845 duration = GST_BUFFER_DURATION (buffer);
3846 if (GST_CLOCK_TIME_IS_VALID (duration)) {
3847 *end = timestamp + duration;
3848 }
3849 *start = timestamp;
3850 }
3851 }
3852
3853 /* must be called with PREROLL_LOCK */
3854 static gboolean
gst_base_sink_needs_preroll(GstBaseSink * basesink)3855 gst_base_sink_needs_preroll (GstBaseSink * basesink)
3856 {
3857 gboolean is_prerolled, res;
3858
3859 /* we have 2 cases where the PREROLL_LOCK is released:
3860 * 1) we are blocking in the PREROLL_LOCK and thus are prerolled.
3861 * 2) we are syncing on the clock
3862 */
3863 #ifdef OHOS_OPT_COMPAT
3864 /** ohos.opt.compat.0054
3865 * Received stream group done, no more buffer to preroll for now.
3866 * Thus, no need preroll for playing change to paused. Preroll when
3867 * paused to playing.
3868 */
3869 is_prerolled = basesink->have_preroll || basesink->priv->received_eos || basesink->stream_group_done;
3870 #else
3871 is_prerolled = basesink->have_preroll || basesink->priv->received_eos;
3872 #endif
3873 res = !is_prerolled;
3874
3875 GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d => needs preroll: %d",
3876 basesink->have_preroll, basesink->priv->received_eos, res);
3877
3878 return res;
3879 }
3880
3881 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
3882 static gchar *
get_sink_type_by_caps(GstBaseSink * basesink)3883 get_sink_type_by_caps(GstBaseSink * basesink)
3884 {
3885 GstBaseSinkPrivate *priv = basesink->priv;
3886 gchar *cap_str = gst_caps_to_string(priv->caps);
3887 if (!cap_str) {
3888 return "unknow-null-caps";
3889 }
3890
3891 gchar *sink_type = strstr(cap_str, "video") ? "video" : strstr(cap_str, "audio") ? "audio" : "unknow";
3892 g_free(cap_str);
3893 return sink_type;
3894 }
3895
3896 static void
kpi_log_render_first_frame(GstBaseSink * basesink)3897 kpi_log_render_first_frame (GstBaseSink *basesink)
3898 {
3899 GstBaseSinkPrivate *priv = basesink->priv;
3900 if (priv->has_render_first_frame) {
3901 return;
3902 }
3903 priv->has_render_first_frame = TRUE;
3904 GST_WARNING_OBJECT (basesink, "KPI-TRACE: render first %s frame", get_sink_type_by_caps(basesink));
3905 }
3906
3907 #define LEVEL_S_TIME 100000 // 100 ms
3908
3909 static void
kpi_log_fps(GstBaseSink * basesink)3910 kpi_log_fps(GstBaseSink *basesink)
3911 {
3912 GstBaseSinkPrivate *priv = basesink->priv;
3913 if (priv->sink_type != SINK_TYPE_VIDEO) {
3914 return;
3915 }
3916
3917 gint64 curtime = g_get_monotonic_time();
3918 if (priv->rendered == 1) {
3919 priv->tmp_render_nums_fps = priv->rendered;
3920 priv->tmp_time_fps = curtime;
3921 priv->kpi_last_render_time = curtime;
3922 return;
3923 }
3924
3925 guint64 fps_time_diff = curtime > priv->tmp_time_fps ? curtime - priv->tmp_time_fps : 0;
3926 if (fps_time_diff >= GST_MSECOND) {
3927 gdouble time_sec = (gdouble)fps_time_diff / GST_MSECOND;
3928 gdouble fps = (priv->rendered - priv->tmp_render_nums_fps) / time_sec;
3929 GST_DEBUG_OBJECT (basesink, "KPI-TRACE: fps=%f, time=%f, render nums=%" G_GUINT64_FORMAT,
3930 fps, time_sec, priv->rendered);
3931 priv->tmp_render_nums_fps = priv->rendered;
3932 priv->tmp_time_fps = curtime;
3933 }
3934
3935 guint64 render_time_diff = curtime > priv->kpi_last_render_time ? curtime - priv->kpi_last_render_time : 0;
3936 if (render_time_diff >= LEVEL_S_TIME) {
3937 priv->late_frames_nums++;
3938 GST_WARNING_OBJECT (basesink, "KPI-TRACE: render_time_diff=%" G_GUINT64_FORMAT
3939 " ms, late_frames_nums=%" G_GUINT64_FORMAT,
3940 render_time_diff / GST_USECOND, priv->late_frames_nums);
3941 }
3942 priv->kpi_last_render_time = curtime;
3943 }
3944 #endif
3945
3946 /* with STREAM_LOCK, PREROLL_LOCK
3947 *
3948 * Takes a buffer and compare the timestamps with the last segment.
3949 * If the buffer falls outside of the segment boundaries, drop it.
3950 * Else send the buffer for preroll and rendering.
3951 *
3952 * This function takes ownership of the buffer.
3953 */
3954 static GstFlowReturn
gst_base_sink_chain_unlocked(GstBaseSink * basesink,GstPad * pad,gpointer obj,gboolean is_list)3955 gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
3956 gpointer obj, gboolean is_list)
3957 {
3958 GstBaseSinkClass *bclass;
3959 GstBaseSinkPrivate *priv = basesink->priv;
3960 GstFlowReturn ret = GST_FLOW_OK;
3961 GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
3962 GstSegment *segment;
3963 GstBuffer *sync_buf;
3964 gboolean late, step_end, prepared = FALSE;
3965
3966 if (G_UNLIKELY (basesink->flushing))
3967 goto flushing;
3968
3969 if (G_UNLIKELY (priv->received_eos))
3970 goto was_eos;
3971
3972 if (is_list) {
3973 GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
3974
3975 if (gst_buffer_list_length (buffer_list) == 0)
3976 goto empty_list;
3977
3978 sync_buf = gst_buffer_list_get (buffer_list, 0);
3979 g_assert (NULL != sync_buf);
3980 } else {
3981 sync_buf = GST_BUFFER_CAST (obj);
3982 }
3983
3984 /* for code clarity */
3985 segment = &basesink->segment;
3986
3987 if (G_UNLIKELY (!basesink->have_newsegment)) {
3988 gboolean sync;
3989
3990 sync = gst_base_sink_get_sync (basesink);
3991 if (sync) {
3992 GST_ELEMENT_WARNING (basesink, STREAM, FAILED,
3993 (_("Internal data flow problem.")),
3994 ("Received buffer without a new-segment. Assuming timestamps start from 0."));
3995 }
3996
3997 /* this means this sink will assume timestamps start from 0 */
3998 GST_OBJECT_LOCK (basesink);
3999 segment->start = 0;
4000 segment->stop = -1;
4001 basesink->segment.start = 0;
4002 basesink->segment.stop = -1;
4003 basesink->have_newsegment = TRUE;
4004 GST_OBJECT_UNLOCK (basesink);
4005 }
4006
4007 bclass = GST_BASE_SINK_GET_CLASS (basesink);
4008
4009 /* check if the buffer needs to be dropped, we first ask the subclass for the
4010 * start and end */
4011 if (bclass->get_times)
4012 bclass->get_times (basesink, sync_buf, &start, &end);
4013
4014 if (!GST_CLOCK_TIME_IS_VALID (start)) {
4015 /* if the subclass does not want sync, we use our own values so that we at
4016 * least clip the buffer to the segment */
4017 gst_base_sink_default_get_times (basesink, sync_buf, &start, &end);
4018 }
4019
4020 GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
4021 ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
4022
4023 /* a dropped buffer does not participate in anything. Buffer can only be
4024 * dropped if their PTS falls completely outside the segment, while we sync
4025 * preferably on DTS */
4026 if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) {
4027 GstClockTime pts = GST_BUFFER_PTS (sync_buf);
4028 GstClockTime pts_end = GST_CLOCK_TIME_NONE;
4029
4030 if (!GST_CLOCK_TIME_IS_VALID (pts))
4031 pts = start;
4032
4033 if (GST_CLOCK_TIME_IS_VALID (end))
4034 pts_end = pts + (end - start);
4035
4036 #ifdef OHOS_EXT_FUNC
4037 /**
4038 * ohos.ext.func.0041
4039 * subtitle drop buffer logic
4040 */
4041 if (bclass->need_drop_buffer) {
4042 if (bclass->need_drop_buffer(basesink, segment, pts, pts_end)) {
4043 goto out_of_segment;
4044 }
4045 } else if (G_UNLIKELY (!gst_segment_clip (segment, GST_FORMAT_TIME, pts, pts_end, NULL, NULL)
4046 && priv->drop_out_of_segment)) {
4047 goto out_of_segment;
4048 }
4049 #else
4050 if (G_UNLIKELY (!gst_segment_clip (segment,
4051 GST_FORMAT_TIME, pts, pts_end, NULL, NULL)
4052 && priv->drop_out_of_segment))
4053 goto out_of_segment;
4054 #endif
4055 }
4056
4057 if (bclass->prepare || bclass->prepare_list) {
4058 gboolean do_sync = TRUE, stepped = FALSE, syncable = TRUE;
4059 GstClockTime sstart, sstop, rstart, rstop, rnext;
4060 GstStepInfo *current;
4061
4062 late = FALSE;
4063 step_end = FALSE;
4064
4065 current = &priv->current_step;
4066 syncable =
4067 gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart,
4068 &rstop, &rnext, &do_sync, &stepped, current, &step_end);
4069
4070 if (G_UNLIKELY (stepped))
4071 goto dropped;
4072
4073 if (syncable && do_sync && gst_base_sink_get_sync (basesink)) {
4074 GstClock *clock;
4075
4076 GST_OBJECT_LOCK (basesink);
4077 clock = GST_ELEMENT_CLOCK (basesink);
4078 if (clock && GST_STATE (basesink) == GST_STATE_PLAYING) {
4079 GstClockTime base_time;
4080 GstClockTime stime;
4081 GstClockTime now;
4082
4083 base_time = GST_ELEMENT_CAST (basesink)->base_time;
4084 stime = base_time + gst_base_sink_adjust_time (basesink, rstart);
4085 now = gst_clock_get_time (clock);
4086 GST_OBJECT_UNLOCK (basesink);
4087
4088 late =
4089 gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
4090 GST_CLOCK_EARLY, GST_CLOCK_DIFF (stime, now), FALSE);
4091 } else {
4092 GST_OBJECT_UNLOCK (basesink);
4093 }
4094 }
4095
4096 /* We are about to prepare the first frame, make sure we have prerolled
4097 * already. This prevent nesting prepare/render calls. */
4098 ret = gst_base_sink_do_preroll (basesink, obj);
4099 if (G_UNLIKELY (ret != GST_FLOW_OK))
4100 goto preroll_failed;
4101
4102 if (G_UNLIKELY (late))
4103 goto dropped;
4104
4105 if (!is_list) {
4106 if (bclass->prepare) {
4107 ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
4108 if (G_UNLIKELY (ret != GST_FLOW_OK))
4109 goto prepare_failed;
4110 }
4111 } else {
4112 if (bclass->prepare_list) {
4113 ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
4114 if (G_UNLIKELY (ret != GST_FLOW_OK))
4115 goto prepare_failed;
4116 }
4117 }
4118
4119 prepared = TRUE;
4120 }
4121
4122 again:
4123 late = FALSE;
4124 step_end = FALSE;
4125
4126 /* synchronize this object, non syncable objects return OK
4127 * immediately. */
4128 ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf),
4129 &late, &step_end);
4130 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0004: drop buffer when it is too late
4131 if (G_UNLIKELY (ret == GST_FLOW_CUSTOM_ERROR)) {
4132 ret = GST_FLOW_OK;
4133 goto dropped;
4134 }
4135 #endif
4136 if (G_UNLIKELY (ret != GST_FLOW_OK))
4137 goto sync_failed;
4138
4139 /* Don't skip if prepare() was called on time */
4140 late = late && !prepared;
4141
4142 /* drop late buffers unconditionally, let's hope it's unlikely */
4143 if (G_UNLIKELY (late))
4144 goto dropped;
4145
4146 if (priv->max_bitrate) {
4147 gsize size;
4148
4149 if (is_list)
4150 size = gst_buffer_list_calculate_size (GST_BUFFER_LIST_CAST (obj));
4151 else
4152 size = gst_buffer_get_size (GST_BUFFER_CAST (obj));
4153
4154 priv->rc_accumulated += size;
4155 priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
4156 8 * GST_SECOND, priv->max_bitrate);
4157 }
4158
4159 GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
4160
4161 if (!is_list) {
4162 /* For buffer lists do not set last buffer for now. */
4163 gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
4164 gst_base_sink_set_last_buffer_list (basesink, NULL);
4165
4166 if (bclass->render)
4167 ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
4168 } else {
4169 GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
4170
4171 if (bclass->render_list)
4172 ret = bclass->render_list (basesink, buffer_list);
4173
4174 /* Set the first buffer and buffer list to be included in last sample */
4175 gst_base_sink_set_last_buffer (basesink, sync_buf);
4176 gst_base_sink_set_last_buffer_list (basesink, buffer_list);
4177 }
4178
4179 if (ret == GST_FLOW_STEP)
4180 goto again;
4181
4182 if (G_UNLIKELY (basesink->flushing))
4183 goto flushing;
4184
4185 priv->rendered++;
4186 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
4187 kpi_log_render_first_frame(basesink);
4188 kpi_log_fps(basesink);
4189 #endif
4190
4191 done:
4192 if (step_end) {
4193 /* the step ended, check if we need to activate a new step */
4194 GST_DEBUG_OBJECT (basesink, "step ended");
4195 stop_stepping (basesink, &basesink->segment, &priv->current_step,
4196 priv->current_rstart, priv->current_rstop, basesink->eos);
4197 goto again;
4198 }
4199
4200 gst_base_sink_perform_qos (basesink, late);
4201
4202 GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj);
4203 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4204
4205 return ret;
4206
4207 /* ERRORS */
4208 flushing:
4209 {
4210 GST_DEBUG_OBJECT (basesink, "sink is flushing");
4211 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4212 return GST_FLOW_FLUSHING;
4213 }
4214 was_eos:
4215 {
4216 GST_DEBUG_OBJECT (basesink, "we are EOS, dropping object, return EOS");
4217 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4218 return GST_FLOW_EOS;
4219 }
4220 empty_list:
4221 {
4222 GST_DEBUG_OBJECT (basesink, "buffer list with no buffers");
4223 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4224 return GST_FLOW_OK;
4225 }
4226 out_of_segment:
4227 {
4228 GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment");
4229 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4230 return GST_FLOW_OK;
4231 }
4232 prepare_failed:
4233 {
4234 GST_DEBUG_OBJECT (basesink, "prepare buffer failed %s",
4235 gst_flow_get_name (ret));
4236 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4237 return ret;
4238 }
4239 sync_failed:
4240 {
4241 GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
4242 goto done;
4243 }
4244 dropped:
4245 {
4246 priv->dropped++;
4247 GST_DEBUG_OBJECT (basesink, "buffer late, dropping");
4248
4249 if (g_atomic_int_get (&priv->qos_enabled)) {
4250 GstMessage *qos_msg;
4251 GstClockTime timestamp, duration;
4252
4253 timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_buf));
4254 duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_buf));
4255
4256 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
4257 "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT
4258 ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
4259 GST_TIME_ARGS (priv->current_rstart),
4260 GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp),
4261 GST_TIME_ARGS (duration));
4262 GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
4263 "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
4264 priv->rendered, priv->dropped);
4265
4266 qos_msg =
4267 gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync,
4268 priv->current_rstart, priv->current_sstart, timestamp, duration);
4269 gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate,
4270 1000000);
4271 gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered,
4272 priv->dropped);
4273 gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg);
4274 }
4275 goto done;
4276 }
4277 preroll_failed:
4278 {
4279 GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret));
4280 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4281 return ret;
4282 }
4283 }
4284
4285 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
4286 static void
kpi_log_recv_first_frame(GstBaseSink * basesink)4287 kpi_log_recv_first_frame(GstBaseSink *basesink)
4288 {
4289 GstBaseSinkPrivate *priv = basesink->priv;
4290 if (priv->has_recv_first_frame) {
4291 return;
4292 }
4293
4294 priv->has_recv_first_frame = TRUE;
4295
4296 /* get sink type by caps */
4297 gchar *sink_type = get_sink_type_by_caps(basesink);
4298 if (strncmp(sink_type, "video", strlen("video")) == 0) {
4299 priv->sink_type = SINK_TYPE_VIDEO;
4300 } else if (strncmp(sink_type, "audio", strlen("audio")) == 0) {
4301 priv->sink_type = SINK_TYPE_AUDIO;
4302 } else {
4303 priv->sink_type = SINK_TYPE_UNKNOWN;
4304 }
4305 GST_WARNING_OBJECT (basesink, "KPI-TRACE: recv first %s frame", sink_type);
4306 }
4307 #endif
4308
4309 /* with STREAM_LOCK
4310 */
4311 static GstFlowReturn
gst_base_sink_chain_main(GstBaseSink * basesink,GstPad * pad,gpointer obj,gboolean is_list)4312 gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad, gpointer obj,
4313 gboolean is_list)
4314 {
4315 GstFlowReturn result;
4316
4317 if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH))
4318 goto wrong_mode;
4319
4320 GST_BASE_SINK_PREROLL_LOCK (basesink);
4321 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
4322 kpi_log_recv_first_frame(basesink);
4323 #endif
4324 result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list);
4325 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
4326
4327 done:
4328 return result;
4329
4330 /* ERRORS */
4331 wrong_mode:
4332 {
4333 GST_OBJECT_LOCK (pad);
4334 GST_WARNING_OBJECT (basesink,
4335 "Push on pad %s:%s, but it was not activated in push mode",
4336 GST_DEBUG_PAD_NAME (pad));
4337 GST_OBJECT_UNLOCK (pad);
4338 gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
4339 /* we don't post an error message this will signal to the peer
4340 * pushing that EOS is reached. */
4341 result = GST_FLOW_EOS;
4342 goto done;
4343 }
4344 }
4345
4346 static GstFlowReturn
gst_base_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)4347 gst_base_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
4348 {
4349 GstBaseSink *basesink;
4350
4351 basesink = GST_BASE_SINK (parent);
4352
4353 #ifdef OHOS_OPT_PERFORMANCE
4354 // ohos.opt.performance.0005
4355 // add trace
4356 GstStartTrace("Sink:chain_main");
4357 GstFlowReturn ret = gst_base_sink_chain_main (basesink, pad, buf, FALSE);
4358 GstFinishTrace();
4359 return ret;
4360 #else
4361 return gst_base_sink_chain_main (basesink, pad, buf, FALSE);
4362 #endif
4363 }
4364
4365 static GstFlowReturn
gst_base_sink_chain_list(GstPad * pad,GstObject * parent,GstBufferList * list)4366 gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
4367 GstBufferList * list)
4368 {
4369 GstBaseSink *basesink;
4370 GstBaseSinkClass *bclass;
4371 GstFlowReturn result;
4372
4373 basesink = GST_BASE_SINK (parent);
4374 bclass = GST_BASE_SINK_GET_CLASS (basesink);
4375
4376 if (G_LIKELY (bclass->render_list)) {
4377 #ifdef OHOS_OPT_PERFORMANCE
4378 // ohos.opt.performance.0005
4379 // add trace
4380 GstStartTrace("Sink:chain_main");
4381 result = gst_base_sink_chain_main (basesink, pad, list, TRUE);
4382 GstFinishTrace();
4383 #else
4384 result = gst_base_sink_chain_main (basesink, pad, list, TRUE);
4385 #endif
4386 } else {
4387 guint i, len;
4388 GstBuffer *buffer;
4389
4390 GST_LOG_OBJECT (pad, "chaining each buffer in list");
4391
4392 len = gst_buffer_list_length (list);
4393
4394 result = GST_FLOW_OK;
4395 for (i = 0; i < len; i++) {
4396 buffer = gst_buffer_list_get (list, i);
4397 #ifdef OHOS_OPT_PERFORMANCE
4398 // ohos.opt.performance.0005
4399 // add trace
4400 GstStartTrace("Sink:chain_main");
4401 result = gst_base_sink_chain_main (basesink, pad,
4402 gst_buffer_ref (buffer), FALSE);
4403 GstFinishTrace();
4404 #else
4405 result = gst_base_sink_chain_main (basesink, pad,
4406 gst_buffer_ref (buffer), FALSE);
4407 #endif
4408 if (result != GST_FLOW_OK)
4409 break;
4410 }
4411 gst_buffer_list_unref (list);
4412 }
4413 return result;
4414 }
4415
4416
4417 static gboolean
gst_base_sink_default_do_seek(GstBaseSink * sink,GstSegment * segment)4418 gst_base_sink_default_do_seek (GstBaseSink * sink, GstSegment * segment)
4419 {
4420 gboolean res = TRUE;
4421
4422 /* update our offset if the start/stop position was updated */
4423 if (segment->format == GST_FORMAT_BYTES) {
4424 segment->time = segment->start;
4425 } else if (segment->start == 0) {
4426 /* seek to start, we can implement a default for this. */
4427 segment->time = 0;
4428 } else {
4429 res = FALSE;
4430 GST_INFO_OBJECT (sink, "Can't do a default seek");
4431 }
4432
4433 return res;
4434 }
4435
4436 #define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))
4437
4438 static gboolean
gst_base_sink_default_prepare_seek_segment(GstBaseSink * sink,GstEvent * event,GstSegment * segment)4439 gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
4440 GstEvent * event, GstSegment * segment)
4441 {
4442 /* By default, we try one of 2 things:
4443 * - For absolute seek positions, convert the requested position to our
4444 * configured processing format and place it in the output segment \
4445 * - For relative seek positions, convert our current (input) values to the
4446 * seek format, adjust by the relative seek offset and then convert back to
4447 * the processing format
4448 */
4449 GstSeekType start_type, stop_type;
4450 gint64 start, stop;
4451 GstSeekFlags flags;
4452 GstFormat seek_format;
4453 gdouble rate;
4454 gboolean update;
4455 gboolean res = TRUE;
4456
4457 gst_event_parse_seek (event, &rate, &seek_format, &flags,
4458 &start_type, &start, &stop_type, &stop);
4459
4460 if (seek_format == segment->format) {
4461 gst_segment_do_seek (segment, rate, seek_format, flags,
4462 start_type, start, stop_type, stop, &update);
4463 return TRUE;
4464 }
4465
4466 if (start_type != GST_SEEK_TYPE_NONE) {
4467 /* FIXME: Handle seek_end by converting the input segment vals */
4468 res =
4469 gst_pad_query_convert (sink->sinkpad, seek_format, start,
4470 segment->format, &start);
4471 start_type = GST_SEEK_TYPE_SET;
4472 }
4473
4474 if (res && stop_type != GST_SEEK_TYPE_NONE) {
4475 /* FIXME: Handle seek_end by converting the input segment vals */
4476 res =
4477 gst_pad_query_convert (sink->sinkpad, seek_format, stop,
4478 segment->format, &stop);
4479 stop_type = GST_SEEK_TYPE_SET;
4480 }
4481
4482 /* And finally, configure our output segment in the desired format */
4483 gst_segment_do_seek (segment, rate, segment->format, flags, start_type, start,
4484 stop_type, stop, &update);
4485
4486 if (!res)
4487 goto no_format;
4488
4489 return res;
4490
4491 no_format:
4492 {
4493 GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted.");
4494 return FALSE;
4495 }
4496 }
4497
4498 /* perform a seek, only executed in pull mode */
4499 static gboolean
gst_base_sink_perform_seek(GstBaseSink * sink,GstPad * pad,GstEvent * event)4500 gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
4501 {
4502 gboolean flush;
4503 gdouble rate;
4504 GstFormat seek_format, dest_format;
4505 GstSeekFlags flags;
4506 GstSeekType start_type, stop_type;
4507 gboolean seekseg_configured = FALSE;
4508 gint64 start, stop;
4509 gboolean update, res = TRUE;
4510 GstSegment seeksegment;
4511
4512 dest_format = sink->segment.format;
4513
4514 if (event) {
4515 GST_DEBUG_OBJECT (sink, "performing seek with event %p", event);
4516 gst_event_parse_seek (event, &rate, &seek_format, &flags,
4517 &start_type, &start, &stop_type, &stop);
4518
4519 flush = flags & GST_SEEK_FLAG_FLUSH;
4520 } else {
4521 GST_DEBUG_OBJECT (sink, "performing seek without event");
4522 flush = FALSE;
4523 }
4524
4525 if (flush) {
4526 GST_DEBUG_OBJECT (sink, "flushing upstream");
4527 gst_pad_push_event (pad, gst_event_new_flush_start ());
4528 gst_base_sink_flush_start (sink, pad);
4529 } else {
4530 GST_DEBUG_OBJECT (sink, "pausing pulling thread");
4531 }
4532
4533 GST_PAD_STREAM_LOCK (pad);
4534
4535 /* If we configured the seeksegment above, don't overwrite it now. Otherwise
4536 * copy the current segment info into the temp segment that we can actually
4537 * attempt the seek with. We only update the real segment if the seek succeeds. */
4538 if (!seekseg_configured) {
4539 memcpy (&seeksegment, &sink->segment, sizeof (GstSegment));
4540
4541 /* now configure the final seek segment */
4542 if (event) {
4543 if (sink->segment.format != seek_format) {
4544 /* OK, here's where we give the subclass a chance to convert the relative
4545 * seek into an absolute one in the processing format. We set up any
4546 * absolute seek above, before taking the stream lock. */
4547 if (!gst_base_sink_default_prepare_seek_segment (sink, event,
4548 &seeksegment)) {
4549 GST_DEBUG_OBJECT (sink,
4550 "Preparing the seek failed after flushing. " "Aborting seek");
4551 res = FALSE;
4552 }
4553 } else {
4554 /* The seek format matches our processing format, no need to ask the
4555 * the subclass to configure the segment. */
4556 gst_segment_do_seek (&seeksegment, rate, seek_format, flags,
4557 start_type, start, stop_type, stop, &update);
4558 }
4559 }
4560 /* Else, no seek event passed, so we're just (re)starting the
4561 current segment. */
4562 }
4563
4564 if (res) {
4565 GST_DEBUG_OBJECT (sink, "segment configured from %" G_GINT64_FORMAT
4566 " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
4567 seeksegment.start, seeksegment.stop, seeksegment.position);
4568
4569 /* do the seek, segment.position contains the new position. */
4570 res = gst_base_sink_default_do_seek (sink, &seeksegment);
4571 }
4572
4573 if (flush) {
4574 GST_DEBUG_OBJECT (sink, "stop flushing upstream");
4575 gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE));
4576 gst_base_sink_flush_stop (sink, pad, TRUE);
4577 } else if (res && sink->running) {
4578 /* we are running the current segment and doing a non-flushing seek,
4579 * close the segment first based on the position. */
4580 GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT
4581 " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.position);
4582 }
4583
4584 /* The subclass must have converted the segment to the processing format
4585 * by now */
4586 if (res && seeksegment.format != dest_format) {
4587 GST_DEBUG_OBJECT (sink, "Subclass failed to prepare a seek segment "
4588 "in the correct format. Aborting seek.");
4589 res = FALSE;
4590 }
4591
4592 GST_INFO_OBJECT (sink, "seeking done %d: %" GST_SEGMENT_FORMAT, res,
4593 &seeksegment);
4594
4595 /* if successful seek, we update our real segment and push
4596 * out the new segment. */
4597 if (res) {
4598 gst_segment_copy_into (&seeksegment, &sink->segment);
4599
4600 if (sink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
4601 gst_element_post_message (GST_ELEMENT (sink),
4602 gst_message_new_segment_start (GST_OBJECT (sink),
4603 sink->segment.format, sink->segment.position));
4604 }
4605 }
4606
4607 sink->priv->discont = TRUE;
4608 sink->running = TRUE;
4609
4610 GST_PAD_STREAM_UNLOCK (pad);
4611
4612 return res;
4613 }
4614
4615 static void
set_step_info(GstBaseSink * sink,GstStepInfo * current,GstStepInfo * pending,guint seqnum,GstFormat format,guint64 amount,gdouble rate,gboolean flush,gboolean intermediate)4616 set_step_info (GstBaseSink * sink, GstStepInfo * current, GstStepInfo * pending,
4617 guint seqnum, GstFormat format, guint64 amount, gdouble rate,
4618 gboolean flush, gboolean intermediate)
4619 {
4620 GST_OBJECT_LOCK (sink);
4621 pending->seqnum = seqnum;
4622 pending->format = format;
4623 pending->amount = amount;
4624 pending->position = 0;
4625 pending->rate = rate;
4626 pending->flush = flush;
4627 pending->intermediate = intermediate;
4628 pending->valid = TRUE;
4629 /* flush invalidates the current stepping segment */
4630 if (flush)
4631 current->valid = FALSE;
4632 GST_OBJECT_UNLOCK (sink);
4633 }
4634
4635 static gboolean
gst_base_sink_perform_step(GstBaseSink * sink,GstPad * pad,GstEvent * event)4636 gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
4637 {
4638 GstBaseSinkPrivate *priv;
4639 GstBaseSinkClass *bclass;
4640 gboolean flush, intermediate;
4641 gdouble rate;
4642 GstFormat format;
4643 guint64 amount;
4644 guint seqnum;
4645 GstStepInfo *pending, *current;
4646 GstMessage *message;
4647
4648 bclass = GST_BASE_SINK_GET_CLASS (sink);
4649 priv = sink->priv;
4650
4651 GST_DEBUG_OBJECT (sink, "performing step with event %p", event);
4652
4653 gst_event_parse_step (event, &format, &amount, &rate, &flush, &intermediate);
4654 seqnum = gst_event_get_seqnum (event);
4655
4656 pending = &priv->pending_step;
4657 current = &priv->current_step;
4658
4659 /* post message first */
4660 message = gst_message_new_step_start (GST_OBJECT (sink), FALSE, format,
4661 amount, rate, flush, intermediate);
4662 gst_message_set_seqnum (message, seqnum);
4663 gst_element_post_message (GST_ELEMENT (sink), message);
4664
4665 if (flush) {
4666 /* we need to call ::unlock before locking PREROLL_LOCK
4667 * since we lock it before going into ::render */
4668 if (bclass->unlock)
4669 bclass->unlock (sink);
4670
4671 GST_BASE_SINK_PREROLL_LOCK (sink);
4672 /* now that we have the PREROLL lock, clear our unlock request */
4673 if (bclass->unlock_stop)
4674 bclass->unlock_stop (sink);
4675
4676 /* update the stepinfo and make it valid */
4677 set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
4678 intermediate);
4679
4680 if (sink->priv->async_enabled) {
4681 /* and we need to commit our state again on the next
4682 * prerolled buffer */
4683 sink->playing_async = TRUE;
4684 priv->pending_step.need_preroll = TRUE;
4685 sink->need_preroll = FALSE;
4686 gst_base_sink_update_start_time (sink);
4687 gst_element_lost_state (GST_ELEMENT_CAST (sink));
4688 } else {
4689 sink->priv->have_latency = TRUE;
4690 sink->need_preroll = FALSE;
4691 }
4692 priv->current_sstart = GST_CLOCK_TIME_NONE;
4693 priv->current_sstop = GST_CLOCK_TIME_NONE;
4694 priv->eos_rtime = GST_CLOCK_TIME_NONE;
4695 priv->call_preroll = TRUE;
4696 gst_base_sink_set_last_buffer (sink, NULL);
4697 gst_base_sink_set_last_buffer_list (sink, NULL);
4698 gst_base_sink_reset_qos (sink);
4699
4700 if (sink->clock_id) {
4701 gst_clock_id_unschedule (sink->clock_id);
4702 }
4703
4704 if (sink->have_preroll) {
4705 GST_DEBUG_OBJECT (sink, "signal waiter");
4706 priv->step_unlock = TRUE;
4707 GST_BASE_SINK_PREROLL_SIGNAL (sink);
4708 }
4709 GST_BASE_SINK_PREROLL_UNLOCK (sink);
4710 } else {
4711 /* update the stepinfo and make it valid */
4712 set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
4713 intermediate);
4714 }
4715
4716 return TRUE;
4717 }
4718
4719 static gboolean
gst_base_sink_perform_instant_rate_change(GstBaseSink * sink,GstPad * pad,GstEvent * event)4720 gst_base_sink_perform_instant_rate_change (GstBaseSink * sink, GstPad * pad,
4721 GstEvent * event)
4722 {
4723 GstBaseSinkPrivate *priv;
4724 guint32 seqnum;
4725 gdouble rate;
4726 GstClockTime running_time, upstream_running_time;
4727
4728 GstClockTime switch_time;
4729 gint res;
4730
4731 priv = sink->priv;
4732
4733 GST_DEBUG_OBJECT (sink, "performing instant-rate-change with event %p",
4734 event);
4735
4736 seqnum = gst_event_get_seqnum (event);
4737 gst_event_parse_instant_rate_sync_time (event, &rate, &running_time,
4738 &upstream_running_time);
4739
4740 GST_DEBUG_OBJECT (sink, "instant-rate-change %u %lf at %" GST_TIME_FORMAT
4741 ", upstream %" GST_TIME_FORMAT,
4742 seqnum, rate, GST_TIME_ARGS (running_time),
4743 GST_TIME_ARGS (upstream_running_time));
4744
4745 /* Take the preroll lock so we can change the segment. We do not call unlock
4746 * like for stepping as that would cause the PLAYING state to be lost and
4747 * would get us into prerolling again first
4748 *
4749 * FIXME: The below potentially blocks until the chain function returns, but
4750 * the lock is not taken during all waiting operations inside the chain
4751 * function (clock, preroll) so this should be fine in most cases. Only
4752 * problem is if the render() or prepare() functions are waiting themselves!
4753 *
4754 * FIXME: If the subclass is calling gst_base_sink_wait() it will be woken
4755 * up but there is no way for it to update the timestamps, or to report back
4756 * to the base class that it should recalculate the values. The current
4757 * change would not be instantaneous in that case but would wait until the
4758 * next buffer.
4759 */
4760 GST_BASE_SINK_PREROLL_LOCK (sink);
4761
4762 /* We can safely change the segment and everything here as we hold the
4763 * PREROLL_LOCK and it is taken for the whole chain function */
4764 sink->priv->instant_rate_sync_seqnum = seqnum;
4765 sink->priv->instant_rate_multiplier = rate;
4766 sink->priv->instant_rate_offset = running_time - upstream_running_time;
4767 sink->priv->last_anchor_running_time = running_time;
4768
4769 GST_DEBUG_OBJECT (sink, "Current internal running time %" GST_TIME_FORMAT
4770 ", last internal running time %" GST_TIME_FORMAT,
4771 GST_TIME_ARGS (running_time),
4772 GST_TIME_ARGS (sink->priv->last_anchor_running_time));
4773
4774 /* Calculate the current position in the segment and do a seek with the
4775 * new rate. This updates rate, base and offset accordingly */
4776 res =
4777 gst_segment_position_from_running_time_full (&sink->segment,
4778 GST_FORMAT_TIME, running_time, &switch_time);
4779
4780 GST_DEBUG_OBJECT (sink, "Before adjustment seg is %" GST_SEGMENT_FORMAT
4781 " new running_time %" GST_TIME_FORMAT
4782 " position %" GST_STIME_FORMAT " res %d", &sink->segment,
4783 GST_TIME_ARGS (running_time),
4784 GST_STIME_ARGS ((GstClockTimeDiff) switch_time), res);
4785
4786 if (res < 0) {
4787 GST_WARNING_OBJECT (sink,
4788 "Negative position calculated. Can't instant-rate change to there");
4789 GST_BASE_SINK_PREROLL_UNLOCK (sink);
4790 return TRUE;
4791 }
4792
4793 sink->segment.position = switch_time;
4794
4795 /* Calculate new output rate based on upstream value */
4796 rate *= sink->priv->upstream_segment.rate;
4797
4798 gst_segment_do_seek (&sink->segment, rate, GST_FORMAT_TIME,
4799 sink->segment.flags & (~GST_SEEK_FLAG_FLUSH) &
4800 GST_SEEK_FLAG_INSTANT_RATE_CHANGE, GST_SEEK_TYPE_NONE, -1,
4801 GST_SEEK_TYPE_NONE, -1, NULL);
4802
4803 GST_DEBUG_OBJECT (sink, "Adjusted segment is now %" GST_SEGMENT_FORMAT,
4804 &sink->segment);
4805
4806 priv->current_sstart = GST_CLOCK_TIME_NONE;
4807 priv->current_sstop = GST_CLOCK_TIME_NONE;
4808 priv->eos_rtime = GST_CLOCK_TIME_NONE;
4809 gst_base_sink_reset_qos (sink);
4810
4811 if (sink->clock_id) {
4812 gst_clock_id_unschedule (sink->clock_id);
4813 }
4814
4815 if (sink->have_preroll) {
4816 GST_DEBUG_OBJECT (sink, "signal waiter");
4817 /* TODO: Rename this, and GST_FLOW_STEP */
4818 priv->step_unlock = TRUE;
4819 GST_BASE_SINK_PREROLL_SIGNAL (sink);
4820 }
4821
4822 GST_BASE_SINK_PREROLL_UNLOCK (sink);
4823
4824 return TRUE;
4825 }
4826
4827 /* with STREAM_LOCK
4828 */
4829 static void
gst_base_sink_loop(GstPad * pad)4830 gst_base_sink_loop (GstPad * pad)
4831 {
4832 GstObject *parent;
4833 GstBaseSink *basesink;
4834 GstBuffer *buf = NULL;
4835 GstFlowReturn result;
4836 guint blocksize;
4837 guint64 offset;
4838
4839 parent = GST_OBJECT_PARENT (pad);
4840 basesink = GST_BASE_SINK (parent);
4841
4842 g_assert (basesink->pad_mode == GST_PAD_MODE_PULL);
4843
4844 if ((blocksize = basesink->priv->blocksize) == 0)
4845 blocksize = -1;
4846
4847 offset = basesink->segment.position;
4848
4849 GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u",
4850 offset, blocksize);
4851
4852 result = gst_pad_pull_range (pad, offset, blocksize, &buf);
4853 if (G_UNLIKELY (result != GST_FLOW_OK))
4854 goto paused;
4855
4856 if (G_UNLIKELY (buf == NULL))
4857 goto no_buffer;
4858
4859 offset += gst_buffer_get_size (buf);
4860
4861 basesink->segment.position = offset;
4862
4863 GST_BASE_SINK_PREROLL_LOCK (basesink);
4864 result = gst_base_sink_chain_unlocked (basesink, pad, buf, FALSE);
4865 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
4866 if (G_UNLIKELY (result != GST_FLOW_OK))
4867 goto paused;
4868
4869 return;
4870
4871 /* ERRORS */
4872 paused:
4873 {
4874 GST_LOG_OBJECT (basesink, "pausing task, reason %s",
4875 gst_flow_get_name (result));
4876 gst_pad_pause_task (pad);
4877 if (result == GST_FLOW_EOS) {
4878 /* perform EOS logic */
4879 if (basesink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
4880 gst_element_post_message (GST_ELEMENT_CAST (basesink),
4881 gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
4882 basesink->segment.format, basesink->segment.position));
4883 gst_base_sink_event (pad, parent,
4884 gst_event_new_segment_done (basesink->segment.format,
4885 basesink->segment.position));
4886 } else {
4887 gst_base_sink_event (pad, parent, gst_event_new_eos ());
4888 }
4889 } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_EOS) {
4890 /* for fatal errors we post an error message, post the error
4891 * first so the app knows about the error first.
4892 * wrong-state is not a fatal error because it happens due to
4893 * flushing and posting an error message in that case is the
4894 * wrong thing to do, e.g. when basesrc is doing a flushing
4895 * seek. */
4896 GST_ELEMENT_FLOW_ERROR (basesink, result);
4897 gst_base_sink_event (pad, parent, gst_event_new_eos ());
4898 }
4899 return;
4900 }
4901 no_buffer:
4902 {
4903 GST_LOG_OBJECT (basesink, "no buffer, pausing");
4904 GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
4905 (_("Internal data flow error.")), ("element returned NULL buffer"));
4906 result = GST_FLOW_ERROR;
4907 goto paused;
4908 }
4909 }
4910
4911 static gboolean
gst_base_sink_set_flushing(GstBaseSink * basesink,GstPad * pad,gboolean flushing)4912 gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
4913 gboolean flushing)
4914 {
4915 GstBaseSinkClass *bclass;
4916
4917 bclass = GST_BASE_SINK_GET_CLASS (basesink);
4918
4919 if (flushing) {
4920 /* unlock any subclasses, we need to do this before grabbing the
4921 * PREROLL_LOCK since we hold this lock before going into ::render. */
4922 if (bclass->unlock)
4923 bclass->unlock (basesink);
4924 }
4925
4926 GST_BASE_SINK_PREROLL_LOCK (basesink);
4927 basesink->flushing = flushing;
4928 if (flushing) {
4929 /* step 1, now that we have the PREROLL lock, clear our unlock request */
4930 if (bclass->unlock_stop)
4931 bclass->unlock_stop (basesink);
4932
4933 /* set need_preroll before we unblock the clock. If the clock is unblocked
4934 * before timing out, we can reuse the buffer for preroll. */
4935 basesink->need_preroll = TRUE;
4936
4937 /* step 2, unblock clock sync (if any) or any other blocking thing */
4938 if (basesink->clock_id) {
4939 gst_clock_id_unschedule (basesink->clock_id);
4940 }
4941
4942 /* flush out the data thread if it's locked in finish_preroll, this will
4943 * also flush out the EOS state */
4944 GST_DEBUG_OBJECT (basesink,
4945 "flushing out data thread, need preroll to TRUE");
4946
4947 /* we can't have EOS anymore now */
4948 basesink->eos = FALSE;
4949 #ifdef OHOS_OPT_COMPAT
4950 /* ohos.opt.compat.0054 */
4951 basesink->stream_group_done = FALSE;
4952 #endif
4953 basesink->priv->received_eos = FALSE;
4954 basesink->have_preroll = FALSE;
4955 basesink->priv->step_unlock = FALSE;
4956 /* can't report latency anymore until we preroll again */
4957 if (basesink->priv->async_enabled) {
4958 GST_OBJECT_LOCK (basesink);
4959 basesink->priv->have_latency = FALSE;
4960 GST_OBJECT_UNLOCK (basesink);
4961 }
4962 /* and signal any waiters now */
4963 GST_BASE_SINK_PREROLL_SIGNAL (basesink);
4964 }
4965 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
4966
4967 return TRUE;
4968 }
4969
4970 static gboolean
gst_base_sink_default_activate_pull(GstBaseSink * basesink,gboolean active)4971 gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active)
4972 {
4973 gboolean result;
4974
4975 if (active) {
4976 /* start task */
4977 result = gst_pad_start_task (basesink->sinkpad,
4978 (GstTaskFunction) gst_base_sink_loop, basesink->sinkpad, NULL);
4979 } else {
4980 /* step 2, make sure streaming finishes */
4981 result = gst_pad_stop_task (basesink->sinkpad);
4982 }
4983
4984 return result;
4985 }
4986
4987 static gboolean
gst_base_sink_pad_activate(GstPad * pad,GstObject * parent)4988 gst_base_sink_pad_activate (GstPad * pad, GstObject * parent)
4989 {
4990 gboolean result = FALSE;
4991 GstBaseSink *basesink;
4992 GstQuery *query;
4993 gboolean pull_mode;
4994
4995 basesink = GST_BASE_SINK (parent);
4996
4997 GST_DEBUG_OBJECT (basesink, "Trying pull mode first");
4998
4999 gst_base_sink_set_flushing (basesink, pad, FALSE);
5000
5001 /* we need to have the pull mode enabled */
5002 if (!basesink->can_activate_pull) {
5003 GST_DEBUG_OBJECT (basesink, "pull mode disabled");
5004 goto fallback;
5005 }
5006
5007 /* check if downstreams supports pull mode at all */
5008 query = gst_query_new_scheduling ();
5009
5010 if (!gst_pad_peer_query (pad, query)) {
5011 gst_query_unref (query);
5012 GST_DEBUG_OBJECT (basesink, "peer query failed, no pull mode");
5013 goto fallback;
5014 }
5015
5016 /* parse result of the query */
5017 pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
5018 gst_query_unref (query);
5019
5020 if (!pull_mode) {
5021 GST_DEBUG_OBJECT (basesink, "pull mode not supported");
5022 goto fallback;
5023 }
5024
5025 /* set the pad mode before starting the task so that it's in the
5026 * correct state for the new thread. also the sink set_caps and get_caps
5027 * function checks this */
5028 basesink->pad_mode = GST_PAD_MODE_PULL;
5029
5030 /* we first try to negotiate a format so that when we try to activate
5031 * downstream, it knows about our format */
5032 if (!gst_base_sink_negotiate_pull (basesink)) {
5033 GST_DEBUG_OBJECT (basesink, "failed to negotiate in pull mode");
5034 goto fallback;
5035 }
5036
5037 /* ok activate now */
5038 if (!gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE)) {
5039 /* clear any pending caps */
5040 GST_OBJECT_LOCK (basesink);
5041 gst_caps_replace (&basesink->priv->caps, NULL);
5042 GST_OBJECT_UNLOCK (basesink);
5043 GST_DEBUG_OBJECT (basesink, "failed to activate in pull mode");
5044 goto fallback;
5045 }
5046
5047 GST_DEBUG_OBJECT (basesink, "Success activating pull mode");
5048 result = TRUE;
5049 goto done;
5050
5051 /* push mode fallback */
5052 fallback:
5053 GST_DEBUG_OBJECT (basesink, "Falling back to push mode");
5054 if ((result = gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE))) {
5055 GST_DEBUG_OBJECT (basesink, "Success activating push mode");
5056 }
5057
5058 done:
5059 if (!result) {
5060 GST_WARNING_OBJECT (basesink, "Could not activate pad in either mode");
5061 gst_base_sink_set_flushing (basesink, pad, TRUE);
5062 }
5063
5064 return result;
5065 }
5066
5067 static gboolean
gst_base_sink_pad_activate_push(GstPad * pad,GstObject * parent,gboolean active)5068 gst_base_sink_pad_activate_push (GstPad * pad, GstObject * parent,
5069 gboolean active)
5070 {
5071 gboolean result;
5072 GstBaseSink *basesink;
5073
5074 basesink = GST_BASE_SINK (parent);
5075
5076 if (active) {
5077 if (!basesink->can_activate_push) {
5078 result = FALSE;
5079 basesink->pad_mode = GST_PAD_MODE_NONE;
5080 } else {
5081 result = TRUE;
5082 basesink->pad_mode = GST_PAD_MODE_PUSH;
5083 }
5084 } else {
5085 if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH)) {
5086 g_warning ("Internal GStreamer activation error!!!");
5087 result = FALSE;
5088 } else {
5089 gst_base_sink_set_flushing (basesink, pad, TRUE);
5090 result = TRUE;
5091 basesink->pad_mode = GST_PAD_MODE_NONE;
5092 }
5093 }
5094
5095 return result;
5096 }
5097
5098 static gboolean
gst_base_sink_negotiate_pull(GstBaseSink * basesink)5099 gst_base_sink_negotiate_pull (GstBaseSink * basesink)
5100 {
5101 GstCaps *caps;
5102 gboolean result;
5103
5104 result = FALSE;
5105
5106 /* this returns the intersection between our caps and the peer caps. If there
5107 * is no peer, it returns %NULL and we can't operate in pull mode so we can
5108 * fail the negotiation. */
5109 caps = gst_pad_get_allowed_caps (GST_BASE_SINK_PAD (basesink));
5110 if (caps == NULL || gst_caps_is_empty (caps))
5111 goto no_caps_possible;
5112
5113 GST_DEBUG_OBJECT (basesink, "allowed caps: %" GST_PTR_FORMAT, caps);
5114
5115 if (gst_caps_is_any (caps)) {
5116 GST_DEBUG_OBJECT (basesink, "caps were ANY after fixating, "
5117 "allowing pull()");
5118 /* neither side has template caps in this case, so they are prepared for
5119 pull() without setcaps() */
5120 result = TRUE;
5121 } else {
5122 /* try to fixate */
5123 caps = gst_base_sink_fixate (basesink, caps);
5124 GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
5125
5126 if (gst_caps_is_fixed (caps)) {
5127 if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
5128 goto could_not_set_caps;
5129
5130 result = TRUE;
5131 }
5132 }
5133
5134 gst_caps_unref (caps);
5135
5136 return result;
5137
5138 no_caps_possible:
5139 {
5140 GST_INFO_OBJECT (basesink, "Pipeline could not agree on caps");
5141 GST_DEBUG_OBJECT (basesink, "get_allowed_caps() returned EMPTY");
5142 if (caps)
5143 gst_caps_unref (caps);
5144 return FALSE;
5145 }
5146 could_not_set_caps:
5147 {
5148 GST_INFO_OBJECT (basesink, "Could not set caps: %" GST_PTR_FORMAT, caps);
5149 gst_caps_unref (caps);
5150 return FALSE;
5151 }
5152 }
5153
5154 /* this won't get called until we implement an activate function */
5155 static gboolean
gst_base_sink_pad_activate_pull(GstPad * pad,GstObject * parent,gboolean active)5156 gst_base_sink_pad_activate_pull (GstPad * pad, GstObject * parent,
5157 gboolean active)
5158 {
5159 gboolean result = FALSE;
5160 GstBaseSink *basesink;
5161 GstBaseSinkClass *bclass;
5162
5163 basesink = GST_BASE_SINK (parent);
5164 bclass = GST_BASE_SINK_GET_CLASS (basesink);
5165
5166 if (active) {
5167 gint64 duration;
5168
5169 /* we mark we have a newsegment here because pull based
5170 * mode works just fine without having a newsegment before the
5171 * first buffer */
5172 gst_segment_init (&basesink->segment, GST_FORMAT_BYTES);
5173 GST_OBJECT_LOCK (basesink);
5174 basesink->have_newsegment = TRUE;
5175 GST_OBJECT_UNLOCK (basesink);
5176
5177 /* get the peer duration in bytes */
5178 result = gst_pad_peer_query_duration (pad, GST_FORMAT_BYTES, &duration);
5179 if (result) {
5180 GST_DEBUG_OBJECT (basesink,
5181 "setting duration in bytes to %" G_GINT64_FORMAT, duration);
5182 basesink->segment.duration = duration;
5183 } else {
5184 GST_DEBUG_OBJECT (basesink, "unknown duration");
5185 }
5186
5187 if (bclass->activate_pull)
5188 result = bclass->activate_pull (basesink, TRUE);
5189 else
5190 result = FALSE;
5191
5192 if (!result)
5193 goto activate_failed;
5194
5195 } else {
5196 if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PULL)) {
5197 g_warning ("Internal GStreamer activation error!!!");
5198 result = FALSE;
5199 } else {
5200 result = gst_base_sink_set_flushing (basesink, pad, TRUE);
5201 if (bclass->activate_pull)
5202 result &= bclass->activate_pull (basesink, FALSE);
5203 basesink->pad_mode = GST_PAD_MODE_NONE;
5204 }
5205 }
5206
5207 return result;
5208
5209 /* ERRORS */
5210 activate_failed:
5211 {
5212 /* reset, as starting the thread failed */
5213 basesink->pad_mode = GST_PAD_MODE_NONE;
5214
5215 GST_ERROR_OBJECT (basesink, "subclass failed to activate in pull mode");
5216 return FALSE;
5217 }
5218 }
5219
5220 static gboolean
gst_base_sink_pad_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)5221 gst_base_sink_pad_activate_mode (GstPad * pad, GstObject * parent,
5222 GstPadMode mode, gboolean active)
5223 {
5224 gboolean res;
5225
5226 switch (mode) {
5227 case GST_PAD_MODE_PULL:
5228 res = gst_base_sink_pad_activate_pull (pad, parent, active);
5229 break;
5230 case GST_PAD_MODE_PUSH:
5231 res = gst_base_sink_pad_activate_push (pad, parent, active);
5232 break;
5233 default:
5234 GST_LOG_OBJECT (pad, "unknown activation mode %d", mode);
5235 res = FALSE;
5236 break;
5237 }
5238 return res;
5239 }
5240
5241 /* send an event to our sinkpad peer. */
5242 static gboolean
gst_base_sink_send_event(GstElement * element,GstEvent * event)5243 gst_base_sink_send_event (GstElement * element, GstEvent * event)
5244 {
5245 GstPad *pad;
5246 GstBaseSink *basesink = GST_BASE_SINK (element);
5247 gboolean forward, result = TRUE;
5248 GstPadMode mode;
5249
5250 GST_OBJECT_LOCK (element);
5251 /* get the pad and the scheduling mode */
5252 pad = gst_object_ref (basesink->sinkpad);
5253 mode = basesink->pad_mode;
5254 GST_OBJECT_UNLOCK (element);
5255
5256 /* only push UPSTREAM events upstream */
5257 forward = GST_EVENT_IS_UPSTREAM (event);
5258
5259 GST_DEBUG_OBJECT (basesink, "handling event %p %" GST_PTR_FORMAT, event,
5260 event);
5261
5262 switch (GST_EVENT_TYPE (event)) {
5263 case GST_EVENT_LATENCY:
5264 {
5265 GstClockTime latency;
5266
5267 gst_event_parse_latency (event, &latency);
5268
5269 /* store the latency. We use this to adjust the running_time before syncing
5270 * it to the clock. */
5271 GST_OBJECT_LOCK (element);
5272 basesink->priv->latency = latency;
5273 if (!basesink->priv->have_latency)
5274 forward = FALSE;
5275 GST_OBJECT_UNLOCK (element);
5276 GST_DEBUG_OBJECT (basesink, "latency set to %" GST_TIME_FORMAT,
5277 GST_TIME_ARGS (latency));
5278
5279 /* We forward this event so that all elements know about the global pipeline
5280 * latency. This is interesting for an element when it wants to figure out
5281 * when a particular piece of data will be rendered. */
5282 break;
5283 }
5284 case GST_EVENT_INSTANT_RATE_SYNC_TIME:
5285 {
5286 gst_base_sink_perform_instant_rate_change (basesink, pad, event);
5287
5288 /* Forward the event. If upstream handles it already, it is supposed to
5289 * send a SEGMENT event with the same seqnum and the final rate before
5290 * the next buffer
5291 */
5292 forward = TRUE;
5293
5294 break;
5295 }
5296 case GST_EVENT_SEEK:
5297 /* in pull mode we will execute the seek */
5298 if (mode == GST_PAD_MODE_PULL)
5299 result = gst_base_sink_perform_seek (basesink, pad, event);
5300 break;
5301 case GST_EVENT_STEP:
5302 result = gst_base_sink_perform_step (basesink, pad, event);
5303 forward = FALSE;
5304 break;
5305 default:
5306 break;
5307 }
5308
5309 if (forward) {
5310 GST_DEBUG_OBJECT (basesink, "sending event %p %" GST_PTR_FORMAT, event,
5311 event);
5312
5313 /* Compensate for any instant-rate-change related running time offset
5314 * between upstream and the internal running time of the sink */
5315 if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) {
5316 GstClockTime now = GST_CLOCK_TIME_NONE;
5317 GstClockTime actual_duration;
5318 GstClockTime upstream_duration;
5319 GstClockTimeDiff difference;
5320 gboolean is_playing, negative_duration;
5321
5322 GST_OBJECT_LOCK (basesink);
5323 is_playing = GST_STATE (basesink) == GST_STATE_PLAYING
5324 && (GST_STATE_PENDING (basesink) == GST_STATE_VOID_PENDING ||
5325 GST_STATE_PENDING (basesink) == GST_STATE_PLAYING);
5326
5327 if (is_playing) {
5328 GstClockTime base_time, clock_time;
5329 GstClock *clock;
5330
5331 base_time = GST_ELEMENT_CAST (basesink)->base_time;
5332 clock = GST_ELEMENT_CLOCK (basesink);
5333 GST_OBJECT_UNLOCK (basesink);
5334
5335 if (clock) {
5336 clock_time = gst_clock_get_time (clock);
5337 now = clock_time - base_time;
5338 }
5339 } else {
5340 now = GST_ELEMENT_START_TIME (basesink);
5341 GST_OBJECT_UNLOCK (basesink);
5342 }
5343
5344 GST_DEBUG_OBJECT (basesink,
5345 "Current internal running time %" GST_TIME_FORMAT
5346 ", last internal running time %" GST_TIME_FORMAT, GST_TIME_ARGS (now),
5347 GST_TIME_ARGS (basesink->priv->last_anchor_running_time));
5348
5349 if (now != GST_CLOCK_TIME_NONE) {
5350 /* Calculate how much running time was spent since the last switch/segment
5351 * in the "corrected upstream segment", our segment */
5352 /* Due to rounding errors and other inaccuracies, it can happen
5353 * that our calculated internal running time is before the upstream
5354 * running time. We need to compensate for that */
5355 if (now < basesink->priv->last_anchor_running_time) {
5356 actual_duration = basesink->priv->last_anchor_running_time - now;
5357 negative_duration = TRUE;
5358 } else {
5359 actual_duration = now - basesink->priv->last_anchor_running_time;
5360 negative_duration = FALSE;
5361 }
5362
5363 /* Transpose that duration (i.e. what upstream beliefs) */
5364 upstream_duration =
5365 (actual_duration * basesink->segment.rate) /
5366 basesink->priv->upstream_segment.rate;
5367
5368 /* Add the difference to the previously accumulated correction */
5369 if (negative_duration)
5370 difference = upstream_duration - actual_duration;
5371 else
5372 difference = actual_duration - upstream_duration;
5373
5374 GST_DEBUG_OBJECT (basesink,
5375 "Current instant rate correction offset. Actual duration %"
5376 GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT
5377 ", negative %d, difference %" GST_STIME_FORMAT ", current offset %"
5378 GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration),
5379 GST_TIME_ARGS (upstream_duration), negative_duration,
5380 GST_STIME_ARGS (difference),
5381 GST_STIME_ARGS (basesink->priv->instant_rate_offset + difference));
5382
5383 difference = basesink->priv->instant_rate_offset + difference;
5384
5385 event = gst_event_make_writable (event);
5386 gst_event_set_running_time_offset (event, -difference);
5387 }
5388 }
5389
5390 result = gst_pad_push_event (pad, event);
5391 } else {
5392 /* not forwarded, unref the event */
5393 gst_event_unref (event);
5394 }
5395
5396 gst_object_unref (pad);
5397
5398 GST_DEBUG_OBJECT (basesink, "handled event: %d", result);
5399
5400 return result;
5401 }
5402
5403 static gboolean
gst_base_sink_get_position(GstBaseSink * basesink,GstFormat format,gint64 * cur,gboolean * upstream)5404 gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
5405 gint64 * cur, gboolean * upstream)
5406 {
5407 GstClock *clock = NULL;
5408 gboolean res = FALSE;
5409 GstFormat oformat;
5410 GstSegment *segment;
5411 GstClockTime now, latency;
5412 GstClockTimeDiff base_time;
5413 gint64 time, base, offset, duration;
5414 gdouble rate;
5415 gint64 last;
5416 gboolean last_seen, with_clock, in_paused;
5417
5418 GST_OBJECT_LOCK (basesink);
5419 /* we can only get the segment when we are not NULL or READY */
5420 if (!basesink->have_newsegment)
5421 goto wrong_state;
5422
5423 in_paused = FALSE;
5424 /* when not in PLAYING or when we're busy with a state change, we
5425 * cannot read from the clock so we report time based on the
5426 * last seen timestamp. */
5427 if (GST_STATE (basesink) != GST_STATE_PLAYING ||
5428 GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) {
5429 in_paused = TRUE;
5430 }
5431
5432 segment = &basesink->segment;
5433
5434 /* get the format in the segment */
5435 oformat = segment->format;
5436
5437 /* report with last seen position when EOS */
5438 last_seen = basesink->eos;
5439
5440 /* assume we will use the clock for getting the current position */
5441 with_clock = TRUE;
5442 if (!basesink->sync)
5443 with_clock = FALSE;
5444
5445 /* and we need a clock */
5446 if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
5447 with_clock = FALSE;
5448 else
5449 gst_object_ref (clock);
5450
5451 /* mainloop might be querying position when going to playing async,
5452 * while (audio) rendering might be quickly advancing stream position,
5453 * so use clock asap rather than last reported position */
5454 if (in_paused && with_clock && g_atomic_int_get (&basesink->priv->to_playing)) {
5455 GST_DEBUG_OBJECT (basesink, "going to PLAYING, so not PAUSED");
5456 in_paused = FALSE;
5457 }
5458
5459 /* collect all data we need holding the lock */
5460 if (GST_CLOCK_TIME_IS_VALID (segment->time))
5461 time = segment->time;
5462 else
5463 time = 0;
5464
5465 if (GST_CLOCK_TIME_IS_VALID (segment->offset))
5466 offset = segment->offset;
5467 else
5468 offset = 0;
5469
5470 if (GST_CLOCK_TIME_IS_VALID (segment->stop))
5471 duration = segment->stop - segment->start;
5472 else
5473 duration = 0;
5474
5475 base = segment->base;
5476 rate = segment->rate * segment->applied_rate;
5477 latency = basesink->priv->latency;
5478
5479 if (in_paused) {
5480 /* in paused, use start_time */
5481 base_time = GST_ELEMENT_START_TIME (basesink);
5482 GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
5483 GST_TIME_ARGS (base_time));
5484 } else if (with_clock) {
5485 /* else use clock when needed */
5486 base_time = GST_ELEMENT_CAST (basesink)->base_time;
5487 GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
5488 GST_TIME_ARGS (base_time));
5489 } else {
5490 /* else, no sync or clock -> no base time */
5491 GST_DEBUG_OBJECT (basesink, "no sync or no clock");
5492 base_time = -1;
5493 }
5494
5495 /* no base_time, we can't calculate running_time, use last seem timestamp to report
5496 * time */
5497 if (base_time == -1)
5498 last_seen = TRUE;
5499
5500 if (oformat == GST_FORMAT_TIME) {
5501 gint64 start, stop;
5502
5503 start = basesink->priv->current_sstart;
5504 stop = basesink->priv->current_sstop;
5505
5506 if (last_seen) {
5507 /* when we don't use the clock, we use the last position as a lower bound */
5508 if (stop == -1 || segment->rate > 0.0)
5509 last = start;
5510 else
5511 last = stop;
5512
5513 GST_DEBUG_OBJECT (basesink, "in PAUSED using last %" GST_TIME_FORMAT,
5514 GST_TIME_ARGS (last));
5515 } else {
5516 /* in playing and paused, use last stop time as upper bound */
5517 if (start == -1 || segment->rate > 0.0)
5518 last = stop;
5519 else
5520 last = start;
5521
5522 GST_DEBUG_OBJECT (basesink, "in PLAYING using last %" GST_TIME_FORMAT,
5523 GST_TIME_ARGS (last));
5524 }
5525 } else {
5526 /* convert position to stream time */
5527 last = gst_segment_to_stream_time (segment, oformat, segment->position);
5528
5529 GST_DEBUG_OBJECT (basesink, "in using last %" G_GINT64_FORMAT, last);
5530 }
5531
5532 /* need to release the object lock before we can get the time,
5533 * a clock might take the LOCK of the provider, which could be
5534 * a basesink subclass. */
5535 GST_OBJECT_UNLOCK (basesink);
5536
5537 if (last_seen) {
5538 /* in EOS or when no valid stream_time, report the value of last seen
5539 * timestamp */
5540 if (last == -1) {
5541 /* no timestamp, we need to ask upstream */
5542 GST_DEBUG_OBJECT (basesink, "no last seen timestamp, asking upstream");
5543 res = FALSE;
5544 *upstream = TRUE;
5545 goto done;
5546 }
5547 GST_DEBUG_OBJECT (basesink, "using last seen timestamp %" GST_TIME_FORMAT,
5548 GST_TIME_ARGS (last));
5549 *cur = last;
5550 } else {
5551 if (oformat != GST_FORMAT_TIME) {
5552 /* convert base, time and duration to time */
5553 if (!gst_pad_query_convert (basesink->sinkpad, oformat, base,
5554 GST_FORMAT_TIME, &base))
5555 goto convert_failed;
5556 if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration,
5557 GST_FORMAT_TIME, &duration))
5558 goto convert_failed;
5559 if (!gst_pad_query_convert (basesink->sinkpad, oformat, time,
5560 GST_FORMAT_TIME, &time))
5561 goto convert_failed;
5562 if (!gst_pad_query_convert (basesink->sinkpad, oformat, last,
5563 GST_FORMAT_TIME, &last))
5564 goto convert_failed;
5565
5566 /* assume time format from now on */
5567 oformat = GST_FORMAT_TIME;
5568 }
5569
5570 if (!in_paused && with_clock) {
5571 now = gst_clock_get_time (clock);
5572 } else {
5573 now = base_time;
5574 base_time = 0;
5575 }
5576
5577 /* subtract base time and base time from the clock time.
5578 * Make sure we don't go negative. This is the current time in
5579 * the segment which we need to scale with the combined
5580 * rate and applied rate. */
5581 base_time += base;
5582 base_time += latency;
5583 if (GST_CLOCK_DIFF (base_time, now) < 0)
5584 base_time = now;
5585
5586 /* for negative rates we need to count back from the segment
5587 * duration. */
5588 if (rate < 0.0)
5589 time += duration;
5590
5591 *cur = time + offset + gst_guint64_to_gdouble (now - base_time) * rate;
5592
5593 /* never report more than last seen position */
5594 if (last != -1) {
5595 if (rate > 0.0)
5596 *cur = MIN (last, *cur);
5597 else
5598 *cur = MAX (last, *cur);
5599 }
5600
5601 GST_DEBUG_OBJECT (basesink,
5602 "now %" GST_TIME_FORMAT " - base_time %" GST_TIME_FORMAT " - base %"
5603 GST_TIME_FORMAT " + time %" GST_TIME_FORMAT " last %" GST_TIME_FORMAT,
5604 GST_TIME_ARGS (now), GST_TIME_ARGS (base_time), GST_TIME_ARGS (base),
5605 GST_TIME_ARGS (time), GST_TIME_ARGS (last));
5606 }
5607
5608 if (oformat != format) {
5609 /* convert to final format */
5610 if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, format, cur))
5611 goto convert_failed;
5612 }
5613
5614 res = TRUE;
5615
5616 done:
5617 GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT,
5618 res, GST_TIME_ARGS (*cur));
5619
5620 if (clock)
5621 gst_object_unref (clock);
5622
5623 return res;
5624
5625 /* special cases */
5626 wrong_state:
5627 {
5628 /* in NULL or READY we always return FALSE and -1 */
5629 GST_DEBUG_OBJECT (basesink, "position in wrong state, return -1");
5630 res = FALSE;
5631 *cur = -1;
5632 GST_OBJECT_UNLOCK (basesink);
5633 goto done;
5634 }
5635 convert_failed:
5636 {
5637 GST_DEBUG_OBJECT (basesink, "convert failed, try upstream");
5638 *upstream = TRUE;
5639 res = FALSE;
5640 goto done;
5641 }
5642 }
5643
5644 static gboolean
gst_base_sink_get_duration(GstBaseSink * basesink,GstFormat format,gint64 * dur,gboolean * upstream)5645 gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format,
5646 gint64 * dur, gboolean * upstream)
5647 {
5648 gboolean res = FALSE;
5649
5650 if (basesink->pad_mode == GST_PAD_MODE_PULL) {
5651 gint64 uduration;
5652
5653 /* get the duration in bytes, in pull mode that's all we are sure to
5654 * know. We have to explicitly get this value from upstream instead of
5655 * using our cached value because it might change. Duration caching
5656 * should be done at a higher level. */
5657 res =
5658 gst_pad_peer_query_duration (basesink->sinkpad, GST_FORMAT_BYTES,
5659 &uduration);
5660 if (res) {
5661 basesink->segment.duration = uduration;
5662 if (format != GST_FORMAT_BYTES) {
5663 /* convert to the requested format */
5664 res =
5665 gst_pad_query_convert (basesink->sinkpad, GST_FORMAT_BYTES,
5666 uduration, format, dur);
5667 } else {
5668 *dur = uduration;
5669 }
5670 }
5671 *upstream = FALSE;
5672 } else {
5673 *upstream = TRUE;
5674 }
5675
5676 return res;
5677 }
5678
5679 static gboolean
default_element_query(GstElement * element,GstQuery * query)5680 default_element_query (GstElement * element, GstQuery * query)
5681 {
5682 gboolean res = FALSE;
5683
5684 GstBaseSink *basesink = GST_BASE_SINK (element);
5685
5686 switch (GST_QUERY_TYPE (query)) {
5687 case GST_QUERY_POSITION:
5688 {
5689 #ifdef OHOS_EXT_FUNC
5690 /**
5691 * ohos.ext.func.0040
5692 * Ignore subtitle sink when quering position.
5693 */
5694 if (basesink->priv->sink_type == SINK_TYPE_UNKNOWN) {
5695 return FALSE;
5696 }
5697 #endif
5698 gint64 cur = 0;
5699 GstFormat format;
5700 gboolean upstream = FALSE;
5701
5702 gst_query_parse_position (query, &format, NULL);
5703
5704 GST_DEBUG_OBJECT (basesink, "position query in format %s",
5705 gst_format_get_name (format));
5706
5707 /* first try to get the position based on the clock */
5708 if ((res =
5709 gst_base_sink_get_position (basesink, format, &cur, &upstream))) {
5710 gst_query_set_position (query, format, cur);
5711 } else if (upstream) {
5712 /* fallback to peer query */
5713 res = gst_pad_peer_query (basesink->sinkpad, query);
5714 }
5715 if (!res) {
5716 /* we can handle a few things if upstream failed */
5717 if (format == GST_FORMAT_PERCENT) {
5718 gint64 dur = 0;
5719
5720 res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur,
5721 &upstream);
5722 if (!res && upstream) {
5723 res =
5724 gst_pad_peer_query_position (basesink->sinkpad, GST_FORMAT_TIME,
5725 &cur);
5726 }
5727 if (res) {
5728 res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur,
5729 &upstream);
5730 if (!res && upstream) {
5731 res =
5732 gst_pad_peer_query_duration (basesink->sinkpad,
5733 GST_FORMAT_TIME, &dur);
5734 }
5735 }
5736 if (res) {
5737 gint64 pos;
5738
5739 pos = gst_util_uint64_scale (100 * GST_FORMAT_PERCENT_SCALE, cur,
5740 dur);
5741 gst_query_set_position (query, GST_FORMAT_PERCENT, pos);
5742 }
5743 }
5744 }
5745 break;
5746 }
5747 case GST_QUERY_DURATION:
5748 {
5749 gint64 dur = 0;
5750 GstFormat format;
5751 gboolean upstream = FALSE;
5752
5753 gst_query_parse_duration (query, &format, NULL);
5754
5755 GST_DEBUG_OBJECT (basesink, "duration query in format %s",
5756 gst_format_get_name (format));
5757
5758 if ((res =
5759 gst_base_sink_get_duration (basesink, format, &dur, &upstream))) {
5760 gst_query_set_duration (query, format, dur);
5761 } else if (upstream) {
5762 /* fallback to peer query */
5763 res = gst_pad_peer_query (basesink->sinkpad, query);
5764 }
5765 if (!res) {
5766 /* we can handle a few things if upstream failed */
5767 if (format == GST_FORMAT_PERCENT) {
5768 gst_query_set_duration (query, GST_FORMAT_PERCENT,
5769 GST_FORMAT_PERCENT_MAX);
5770 res = TRUE;
5771 }
5772 }
5773 break;
5774 }
5775 case GST_QUERY_LATENCY:
5776 {
5777 gboolean live, us_live;
5778 GstClockTime min, max;
5779
5780 if ((res = gst_base_sink_query_latency (basesink, &live, &us_live, &min,
5781 &max))) {
5782 gst_query_set_latency (query, live, min, max);
5783 }
5784 break;
5785 }
5786 case GST_QUERY_JITTER:
5787 break;
5788 case GST_QUERY_RATE:
5789 /* gst_query_set_rate (query, basesink->segment_rate); */
5790 res = TRUE;
5791 break;
5792 case GST_QUERY_SEGMENT:
5793 {
5794 if (basesink->pad_mode == GST_PAD_MODE_PULL) {
5795 GstFormat format;
5796 gint64 start, stop;
5797
5798 format = basesink->segment.format;
5799
5800 start =
5801 gst_segment_to_stream_time (&basesink->segment, format,
5802 basesink->segment.start);
5803 if ((stop = basesink->segment.stop) == -1)
5804 stop = basesink->segment.duration;
5805 else
5806 stop = gst_segment_to_stream_time (&basesink->segment, format, stop);
5807
5808 gst_query_set_segment (query, basesink->segment.rate, format, start,
5809 stop);
5810 res = TRUE;
5811 } else {
5812 res = gst_pad_peer_query (basesink->sinkpad, query);
5813 }
5814 break;
5815 }
5816 case GST_QUERY_SEEKING:
5817 case GST_QUERY_CONVERT:
5818 case GST_QUERY_FORMATS:
5819 default:
5820 res = gst_pad_peer_query (basesink->sinkpad, query);
5821 break;
5822 }
5823 GST_DEBUG_OBJECT (basesink, "query %s returns %d",
5824 GST_QUERY_TYPE_NAME (query), res);
5825 return res;
5826 }
5827
5828 static void
gst_base_sink_drain(GstBaseSink * basesink)5829 gst_base_sink_drain (GstBaseSink * basesink)
5830 {
5831 GstBuffer *old;
5832 GstBufferList *old_list;
5833
5834 GST_OBJECT_LOCK (basesink);
5835 if ((old = basesink->priv->last_buffer))
5836 basesink->priv->last_buffer = gst_buffer_copy_deep (old);
5837
5838 if ((old_list = basesink->priv->last_buffer_list))
5839 basesink->priv->last_buffer_list = gst_buffer_list_copy_deep (old_list);
5840 GST_OBJECT_UNLOCK (basesink);
5841
5842 if (old)
5843 gst_buffer_unref (old);
5844 if (old_list)
5845 gst_mini_object_unref (GST_MINI_OBJECT_CAST (old_list));
5846 }
5847
5848 static gboolean
gst_base_sink_default_query(GstBaseSink * basesink,GstQuery * query)5849 gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
5850 {
5851 gboolean res;
5852 GstBaseSinkClass *bclass;
5853
5854 bclass = GST_BASE_SINK_GET_CLASS (basesink);
5855
5856 switch (GST_QUERY_TYPE (query)) {
5857 case GST_QUERY_ALLOCATION:
5858 {
5859 gst_base_sink_drain (basesink);
5860 if (bclass->propose_allocation)
5861 res = bclass->propose_allocation (basesink, query);
5862 else
5863 res = FALSE;
5864 break;
5865 }
5866 case GST_QUERY_CAPS:
5867 {
5868 GstCaps *caps, *filter;
5869
5870 gst_query_parse_caps (query, &filter);
5871 caps = gst_base_sink_query_caps (basesink, basesink->sinkpad, filter);
5872 gst_query_set_caps_result (query, caps);
5873 gst_caps_unref (caps);
5874 res = TRUE;
5875 break;
5876 }
5877 case GST_QUERY_ACCEPT_CAPS:
5878 {
5879 GstCaps *caps, *allowed;
5880 gboolean subset;
5881
5882 /* slightly faster than the default implementation */
5883 gst_query_parse_accept_caps (query, &caps);
5884 allowed = gst_base_sink_query_caps (basesink, basesink->sinkpad, NULL);
5885 subset = gst_caps_is_subset (caps, allowed);
5886 GST_DEBUG_OBJECT (basesink, "Checking if requested caps %" GST_PTR_FORMAT
5887 " are a subset of pad caps %" GST_PTR_FORMAT " result %d", caps,
5888 allowed, subset);
5889 gst_caps_unref (allowed);
5890 gst_query_set_accept_caps_result (query, subset);
5891 res = TRUE;
5892 break;
5893 }
5894 case GST_QUERY_DRAIN:
5895 {
5896 gst_base_sink_drain (basesink);
5897 res = TRUE;
5898 break;
5899 }
5900 case GST_QUERY_POSITION:
5901 {
5902 res = default_element_query (GST_ELEMENT (basesink), query);
5903 break;
5904 }
5905 default:
5906 res =
5907 gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink),
5908 query);
5909 break;
5910 }
5911 return res;
5912 }
5913
5914 static gboolean
gst_base_sink_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)5915 gst_base_sink_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
5916 {
5917 GstBaseSink *basesink;
5918 GstBaseSinkClass *bclass;
5919 gboolean res;
5920
5921 basesink = GST_BASE_SINK_CAST (parent);
5922 bclass = GST_BASE_SINK_GET_CLASS (basesink);
5923
5924 if (bclass->query)
5925 res = bclass->query (basesink, query);
5926 else
5927 res = FALSE;
5928
5929 return res;
5930 }
5931
5932 static GstStateChangeReturn
gst_base_sink_change_state(GstElement * element,GstStateChange transition)5933 gst_base_sink_change_state (GstElement * element, GstStateChange transition)
5934 {
5935 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
5936 GstBaseSink *basesink = GST_BASE_SINK (element);
5937 GstBaseSinkClass *bclass;
5938 GstBaseSinkPrivate *priv;
5939
5940 priv = basesink->priv;
5941
5942 bclass = GST_BASE_SINK_GET_CLASS (basesink);
5943
5944 switch (transition) {
5945 case GST_STATE_CHANGE_NULL_TO_READY:
5946 if (bclass->start)
5947 if (!bclass->start (basesink))
5948 goto start_failed;
5949 break;
5950 case GST_STATE_CHANGE_READY_TO_PAUSED:
5951 /* need to complete preroll before this state change completes, there
5952 * is no data flow in READY so we can safely assume we need to preroll. */
5953 GST_BASE_SINK_PREROLL_LOCK (basesink);
5954 GST_DEBUG_OBJECT (basesink, "READY to PAUSED");
5955 basesink->have_newsegment = FALSE;
5956 gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
5957 gst_segment_init (&basesink->priv->upstream_segment,
5958 GST_FORMAT_UNDEFINED);
5959 basesink->offset = 0;
5960 basesink->have_preroll = FALSE;
5961 priv->step_unlock = FALSE;
5962 basesink->need_preroll = TRUE;
5963 basesink->playing_async = TRUE;
5964 priv->current_sstart = GST_CLOCK_TIME_NONE;
5965 priv->current_sstop = GST_CLOCK_TIME_NONE;
5966 priv->eos_rtime = GST_CLOCK_TIME_NONE;
5967 priv->latency = 0;
5968 basesink->eos = FALSE;
5969 #ifdef OHOS_OPT_COMPAT
5970 /* ohos.opt.compat.0054 */
5971 basesink->stream_group_done = FALSE;
5972 #endif
5973 priv->received_eos = FALSE;
5974 gst_base_sink_reset_qos (basesink);
5975 priv->rc_next = -1;
5976 priv->committed = FALSE;
5977 priv->call_preroll = TRUE;
5978 priv->current_step.valid = FALSE;
5979 priv->pending_step.valid = FALSE;
5980 priv->instant_rate_sync_seqnum = GST_SEQNUM_INVALID;
5981 priv->instant_rate_multiplier = 0;
5982 priv->last_instant_rate_seqnum = GST_SEQNUM_INVALID;
5983 priv->segment_seqnum = GST_SEQNUM_INVALID;
5984 priv->instant_rate_offset = 0;
5985 priv->last_anchor_running_time = 0;
5986 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
5987 priv->has_render_first_frame = FALSE;
5988 priv->has_recv_first_frame = FALSE;
5989 #endif
5990 if (priv->async_enabled) {
5991 GST_DEBUG_OBJECT (basesink, "doing async state change");
5992 /* when async enabled, post async-start message and return ASYNC from
5993 * the state change function */
5994 ret = GST_STATE_CHANGE_ASYNC;
5995 gst_element_post_message (GST_ELEMENT_CAST (basesink),
5996 gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
5997 } else {
5998 priv->have_latency = TRUE;
5999 gst_element_post_message (GST_ELEMENT_CAST (basesink),
6000 gst_message_new_latency (GST_OBJECT_CAST (basesink)));
6001 }
6002 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
6003 break;
6004 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
6005 GST_BASE_SINK_PREROLL_LOCK (basesink);
6006 g_atomic_int_set (&basesink->priv->to_playing, TRUE);
6007 if (!gst_base_sink_needs_preroll (basesink)) {
6008 GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, don't need preroll");
6009 /* no preroll needed anymore now. */
6010 basesink->playing_async = FALSE;
6011 basesink->need_preroll = FALSE;
6012 if (basesink->eos) {
6013 GstMessage *message;
6014
6015 /* need to post EOS message here */
6016 GST_DEBUG_OBJECT (basesink, "Now posting EOS");
6017 message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
6018 gst_message_set_seqnum (message, basesink->priv->seqnum);
6019 gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
6020 } else {
6021 GST_DEBUG_OBJECT (basesink, "signal preroll");
6022 GST_BASE_SINK_PREROLL_SIGNAL (basesink);
6023 }
6024 } else {
6025 GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled");
6026 basesink->need_preroll = TRUE;
6027 basesink->playing_async = TRUE;
6028 priv->call_preroll = TRUE;
6029 priv->committed = FALSE;
6030 if (priv->async_enabled) {
6031 GST_DEBUG_OBJECT (basesink, "doing async state change");
6032 ret = GST_STATE_CHANGE_ASYNC;
6033 gst_element_post_message (GST_ELEMENT_CAST (basesink),
6034 gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
6035 }
6036 }
6037 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
6038 break;
6039 default:
6040 break;
6041 }
6042
6043 {
6044 GstStateChangeReturn bret;
6045
6046 bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6047 if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
6048 goto activate_failed;
6049 }
6050
6051 switch (transition) {
6052 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
6053 /* completed transition, so need not be marked any longer
6054 * And it should be unmarked, since e.g. losing our position upon flush
6055 * does not really change state to PAUSED ... */
6056 g_atomic_int_set (&basesink->priv->to_playing, FALSE);
6057 break;
6058 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
6059 g_atomic_int_set (&basesink->priv->to_playing, FALSE);
6060 GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED");
6061 /* FIXME, make sure we cannot enter _render first */
6062
6063 /* we need to call ::unlock before locking PREROLL_LOCK
6064 * since we lock it before going into ::render */
6065 if (bclass->unlock)
6066 bclass->unlock (basesink);
6067
6068 GST_BASE_SINK_PREROLL_LOCK (basesink);
6069 GST_DEBUG_OBJECT (basesink, "got preroll lock");
6070 /* now that we have the PREROLL lock, clear our unlock request */
6071 if (bclass->unlock_stop)
6072 bclass->unlock_stop (basesink);
6073
6074 if (basesink->clock_id) {
6075 GST_DEBUG_OBJECT (basesink, "unschedule clock");
6076 gst_clock_id_unschedule (basesink->clock_id);
6077 }
6078
6079 /* if we don't have a preroll buffer we need to wait for a preroll and
6080 * return ASYNC. */
6081 if (!gst_base_sink_needs_preroll (basesink)) {
6082 GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are prerolled");
6083 basesink->playing_async = FALSE;
6084 basesink->need_preroll = FALSE;
6085 } else {
6086 if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) {
6087 GST_DEBUG_OBJECT (basesink, "element is <= READY");
6088 ret = GST_STATE_CHANGE_SUCCESS;
6089 } else {
6090 GST_DEBUG_OBJECT (basesink,
6091 "PLAYING to PAUSED, we are not prerolled");
6092 basesink->playing_async = TRUE;
6093 basesink->need_preroll = TRUE;
6094 priv->committed = FALSE;
6095 priv->call_preroll = TRUE;
6096 if (priv->async_enabled) {
6097 GST_DEBUG_OBJECT (basesink, "doing async state change");
6098 ret = GST_STATE_CHANGE_ASYNC;
6099 gst_element_post_message (GST_ELEMENT_CAST (basesink),
6100 gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
6101 }
6102 }
6103 }
6104 GST_DEBUG_OBJECT (basesink, "rendered: %" G_GUINT64_FORMAT
6105 ", dropped: %" G_GUINT64_FORMAT, priv->rendered, priv->dropped);
6106
6107 gst_base_sink_reset_qos (basesink);
6108 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
6109 break;
6110 case GST_STATE_CHANGE_PAUSED_TO_READY:
6111 GST_BASE_SINK_PREROLL_LOCK (basesink);
6112 /* start by resetting our position state with the object lock so that the
6113 * position query gets the right idea. We do this before we post the
6114 * messages so that the message handlers pick this up. */
6115 GST_OBJECT_LOCK (basesink);
6116 basesink->have_newsegment = FALSE;
6117 priv->current_sstart = GST_CLOCK_TIME_NONE;
6118 priv->current_sstop = GST_CLOCK_TIME_NONE;
6119 priv->have_latency = FALSE;
6120 #ifdef OHOS_OPT_PERFORMANCE // ohos.opt.performance.0001: add log for kpi
6121 priv->has_render_first_frame = FALSE;
6122 priv->has_recv_first_frame = FALSE;
6123 #endif
6124 if (priv->cached_clock_id) {
6125 gst_clock_id_unref (priv->cached_clock_id);
6126 priv->cached_clock_id = NULL;
6127 }
6128 gst_caps_replace (&basesink->priv->caps, NULL);
6129 GST_OBJECT_UNLOCK (basesink);
6130
6131 gst_base_sink_set_last_buffer (basesink, NULL);
6132 gst_base_sink_set_last_buffer_list (basesink, NULL);
6133 priv->call_preroll = FALSE;
6134
6135 if (!priv->committed) {
6136 if (priv->async_enabled) {
6137 GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done");
6138
6139 gst_element_post_message (GST_ELEMENT_CAST (basesink),
6140 gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
6141 GST_STATE_PLAYING, GST_STATE_PAUSED, GST_STATE_READY));
6142
6143 gst_element_post_message (GST_ELEMENT_CAST (basesink),
6144 gst_message_new_async_done (GST_OBJECT_CAST (basesink),
6145 GST_CLOCK_TIME_NONE));
6146 }
6147 priv->committed = TRUE;
6148 } else {
6149 GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll");
6150 }
6151 GST_BASE_SINK_PREROLL_UNLOCK (basesink);
6152 break;
6153 case GST_STATE_CHANGE_READY_TO_NULL:
6154 if (bclass->stop) {
6155 if (!bclass->stop (basesink)) {
6156 GST_WARNING_OBJECT (basesink, "failed to stop");
6157 }
6158 }
6159 gst_base_sink_set_last_buffer (basesink, NULL);
6160 gst_base_sink_set_last_buffer_list (basesink, NULL);
6161 priv->call_preroll = FALSE;
6162 break;
6163 default:
6164 break;
6165 }
6166
6167 return ret;
6168
6169 /* ERRORS */
6170 start_failed:
6171 {
6172 GST_DEBUG_OBJECT (basesink, "failed to start");
6173 /* subclass is supposed to post a message but we post one as a fallback
6174 * just in case */
6175 GST_ELEMENT_ERROR (basesink, CORE, STATE_CHANGE, (NULL),
6176 ("Failed to start"));
6177 return GST_STATE_CHANGE_FAILURE;
6178 }
6179 activate_failed:
6180 {
6181 GST_DEBUG_OBJECT (basesink,
6182 "element failed to change states -- activation problem?");
6183 return GST_STATE_CHANGE_FAILURE;
6184 }
6185 }
6186
6187 /**
6188 * gst_base_sink_get_stats:
6189 * @sink: #GstBaseSink
6190 *
6191 * Return various #GstBaseSink statistics. This function returns a #GstStructure
6192 * with name `application/x-gst-base-sink-stats` with the following fields:
6193 *
6194 * - "average-rate" G_TYPE_DOUBLE average frame rate
6195 * - "dropped" G_TYPE_UINT64 Number of dropped frames
6196 * - "rendered" G_TYPE_UINT64 Number of rendered frames
6197 *
6198 * Returns: (transfer full): pointer to #GstStructure
6199 *
6200 * Since: 1.18
6201 */
6202 GstStructure *
gst_base_sink_get_stats(GstBaseSink * sink)6203 gst_base_sink_get_stats (GstBaseSink * sink)
6204 {
6205 GstBaseSinkPrivate *priv = NULL;
6206
6207 g_return_val_if_fail (sink != NULL, NULL);
6208 priv = sink->priv;
6209 return gst_structure_new ("application/x-gst-base-sink-stats",
6210 "average-rate", G_TYPE_DOUBLE, priv->avg_rate,
6211 "dropped", G_TYPE_UINT64, priv->dropped,
6212 "rendered", G_TYPE_UINT64, priv->rendered, NULL);
6213 }
6214