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