gstbasesink.c 145 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
/* GStreamer
2
 * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com>
Wim Taymans's avatar
Wim Taymans committed
3
 *
4
 * gstbasesink.c: Base class for sink elements
Wim Taymans's avatar
Wim Taymans committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
23
24
/**
 * SECTION:gstbasesink
 * @short_description: Base class for sink elements
Stefan Kost's avatar
Stefan Kost committed
25
 * @see_also: #GstBaseTransform, #GstBaseSrc
26
 *
27
 * #GstBaseSink is the base class for sink elements in GStreamer, such as
28
 * xvimagesink or filesink. It is a layer on top of #GstElement that provides a
29
30
31
32
33
34
 * simplified interface to plugin writers. #GstBaseSink handles many details
 * for you, for example: preroll, clock synchronization, state changes,
 * activation in push or pull mode, and queries.
 *
 * In most cases, when writing sink elements, there is no need to implement
 * class methods from #GstElement or to set functions on pads, because the
35
36
 * #GstBaseSink infrastructure should be sufficient.
 *
37
 * #GstBaseSink provides support for exactly one sink pad, which should be
38
 * named "sink". A sink implementation (subclass of #GstBaseSink) should
39
 * install a pad template in its base_init function, like so:
Stefan Kost's avatar
Stefan Kost committed
40
 * |[
41
 * static void
42
 * my_element_base_init (gpointer g_class)
43
 * {
44
 *   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
Wim Taymans's avatar
Wim Taymans committed
45
 *
46
47
48
49
50
51
52
 *   // sinktemplate should be a #GstStaticPadTemplate with direction
 *   // #GST_PAD_SINK and name "sink"
 *   gst_element_class_add_pad_template (gstelement_class,
 *       gst_static_pad_template_get (&amp;sinktemplate));
 *   // see #GstElementDetails
 *   gst_element_class_set_details (gstelement_class, &amp;details);
 * }
Stefan Kost's avatar
Stefan Kost committed
53
 * ]|
54
 *
55
 * #GstBaseSink will handle the prerolling correctly. This means that it will
56
 * return #GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
57
 * buffer arrives in this element. The base class will call the
Stefan Kost's avatar
Stefan Kost committed
58
59
 * #GstBaseSinkClass.preroll() vmethod with this preroll buffer and will then
 * commit the state change to the next asynchronously pending state.
60
 *
61
 * When the element is set to PLAYING, #GstBaseSink will synchronise on the
Stefan Kost's avatar
Stefan Kost committed
62
63
64
65
 * clock using the times returned from #GstBaseSinkClass.get_times(). If this
 * function returns #GST_CLOCK_TIME_NONE for the start time, no synchronisation
 * will be done. Synchronisation can be disabled entirely by setting the object
 * #GstBaseSink:sync property to %FALSE.
66
 *
Stefan Kost's avatar
Stefan Kost committed
67
68
 * After synchronisation the virtual method #GstBaseSinkClass.render() will be
 * called. Subclasses should minimally implement this method.
69
 *
Stefan Kost's avatar
Stefan Kost committed
70
71
72
73
74
75
 * Since 0.10.3 subclasses that synchronise on the clock in the
 * #GstBaseSinkClass.render() method are supported as well. These classes
 * typically receive a buffer in the render method and can then potentially
 * block on the clock while rendering. A typical example is an audiosink.
 * Since 0.10.11 these subclasses can use gst_base_sink_wait_preroll() to
 * perform the blocking wait.
76
 *
77
78
 * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait
 * for the clock to reach the time indicated by the stop time of the last
Stefan Kost's avatar
Stefan Kost committed
79
80
81
 * #GstBaseSinkClass.get_times() call before posting an EOS message. When the
 * element receives EOS in PAUSED, preroll completes, the event is queued and an
 * EOS message is posted when going to PLAYING.
82
 *
83
 * #GstBaseSink will internally use the #GST_EVENT_NEWSEGMENT events to schedule
84
 * synchronisation and clipping of buffers. Buffers that fall completely outside
85
86
 * of the current segment are dropped. Buffers that fall partially in the
 * segment are rendered (and prerolled). Subclasses should do any subbuffer
87
 * clipping themselves when needed.
88
89
 *
 * #GstBaseSink will by default report the current playback position in
90
 * #GST_FORMAT_TIME based on the current clock time and segment information.
91
 * If no clock has been set on the element, the query will be forwarded
92
 * upstream.
93
 *
Stefan Kost's avatar
Stefan Kost committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
 * The #GstBaseSinkClass.set_caps() function will be called when the subclass
 * should configure itself to process a specific media type.
 *
 * The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
 * will be called when resources should be allocated. Any 
 * #GstBaseSinkClass.preroll(), #GstBaseSinkClass.render() and
 * #GstBaseSinkClass.set_caps() function will be called between the
 * #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() calls.
 *
 * The #GstBaseSinkClass.event() virtual method will be called when an event is
 * received by #GstBaseSink. Normally this method should only be overriden by
 * very specific elements (such as file sinks) which need to handle the
 * newsegment event specially.
 *
 * #GstBaseSink provides an overridable #GstBaseSinkClass.buffer_alloc()
 * function that can be used by sinks that want to do reverse negotiation or to
 * provide custom buffers (hardware buffers for example) to upstream elements.
 *
 * The #GstBaseSinkClass.unlock() method is called when the elements should
 * unblock any blocking operations they perform in the
 * #GstBaseSinkClass.render() method. This is mostly useful when the
 * #GstBaseSinkClass.render() method performs a blocking write on a file
 * descriptor, for example.
 *
 * The #GstBaseSink:max-lateness property affects how the sink deals with
 * buffers that arrive too late in the sink. A buffer arrives too late in the
 * sink when the presentation time (as a combination of the last segment, buffer
121
122
 * timestamp and element base_time) plus the duration is before the current
 * 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.
Stefan Kost's avatar
Stefan Kost committed
125
126
127
 * This feature is disabled if sync is disabled, the
 * #GstBaseSinkClass.get_times() method does not return a valid start time or
 * 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
 *
Stefan Kost's avatar
Stefan Kost committed
131
132
133
 * The #GstBaseSink:qos property will enable the quality-of-service features of
 * the basesink which gather statistics about the real-time performance of the
 * 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
136
 * information can then be used by upstream elements to reduce their processing
 * rate, for example.
137
 *
Stefan Kost's avatar
Stefan Kost committed
138
139
140
 * Since 0.10.15 the #GstBaseSink:async property can be used to instruct the
 * sink to never perform an ASYNC state change. This feature is mostly usable
 * when dealing with non-synchronized streams or sparse streams.
141
142
 *
 * Last reviewed on 2007-08-29 (0.10.15)
143
144
 */

Wim Taymans's avatar
Wim Taymans committed
145
146
147
148
149
150
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "gstbasesink.h"
#include <gst/gstmarshal.h>
151
#include <gst/gst_private.h>
152
#include <gst/gst-i18n-lib.h>
Wim Taymans's avatar
Wim Taymans committed
153

154
155
GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
#define GST_CAT_DEFAULT gst_base_sink_debug
Wim Taymans's avatar
Wim Taymans committed
156

157
158
159
#define GST_BASE_SINK_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))

160
161
#define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR

162
163
typedef struct
{
164
165
166
167
  gboolean valid;               /* if this info is valid */
  guint32 seqnum;               /* the seqnum of the STEP event */
  GstFormat format;             /* the format of the amount */
  guint64 amount;               /* the total amount of data to skip */
168
  guint64 position;             /* the position in the stepped data */
169
170
171
  guint64 duration;             /* the duration in time of the skipped data */
  guint64 start;                /* running_time of the start */
  gdouble rate;                 /* rate of skipping */
172
  gdouble start_rate;           /* rate before skipping */
173
  guint64 start_start;          /* start position skipping */
174
  guint64 start_stop;           /* stop position skipping */
175
  gboolean flush;               /* if this was a flushing step */
176
177
  gboolean intermediate;        /* if this is an intermediate step */
  gboolean need_preroll;        /* if we need preroll after this step */
178
179
} GstStepInfo;

180
/* FIXME, some stuff in ABI.data and other in Private...
181
182
183
184
 * Make up your mind please.
 */
struct _GstBaseSinkPrivate
{
185
  gint qos_enabled;             /* ATOMIC */
186
  gboolean async_enabled;
187
  GstClockTimeDiff ts_offset;
188
  GstClockTime render_delay;
189

190
191
192
  /* start, stop of current buffer, stream time, used to report position */
  GstClockTime current_sstart;
  GstClockTime current_sstop;
193

194
195
196
  /* start, stop and jitter of current buffer, running time */
  GstClockTime current_rstart;
  GstClockTime current_rstop;
197
198
  GstClockTimeDiff current_jitter;

199
200
201
  /* EOS sync time in running time */
  GstClockTime eos_rtime;

202
  /* last buffer that arrived in time, running time */
203
  GstClockTime last_in_time;
204
  /* when the last buffer left the sink, running time */
205
206
  GstClockTime last_left;

207
  /* running averages go here these are done on running time */
208
209
210
211
  GstClockTime avg_pt;
  GstClockTime avg_duration;
  gdouble avg_rate;

212
  /* these are done on system time. avg_jitter and avg_render are
213
   * compared to eachother to see if the rendering time takes a
214
   * huge amount of the processing, If so we are flooded with
215
216
217
   * buffers. */
  GstClockTime last_left_systime;
  GstClockTime avg_jitter;
218
  GstClockTime start, stop;
219
  GstClockTime avg_render;
220
221
222
223

  /* number of rendered and dropped frames */
  guint64 rendered;
  guint64 dropped;
224
225
226

  /* latency stuff */
  GstClockTime latency;
227

228
  /* if we already commited the state */
229
  gboolean commited;
230
231
232

  /* when we received EOS */
  gboolean received_eos;
233
234
235

  /* when we are prerolled and able to report latency */
  gboolean have_latency;
236
237
238

  /* the last buffer we prerolled or rendered. Useful for making snapshots */
  GstBuffer *last_buffer;
239
240
241
242

  /* caps for pull based scheduling */
  GstCaps *pull_caps;

243
  /* blocksize for pulling */
244
  guint blocksize;
245
246

  gboolean discont;
247
248
249

  /* seqnum of the stream */
  guint32 seqnum;
250
251

  gboolean call_preroll;
252
  gboolean step_unlock;
253

254
255
256
  /* we have a pending and a current step operation */
  GstStepInfo current_step;
  GstStepInfo pending_step;
257
258
};

259
260
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))

261
/* generic running average, this has a neutral window size */
262
263
#define UPDATE_RUNNING_AVG(avg,val)   DO_RUNNING_AVG(avg,val,8)

264
265
266
/* the windows for these running averages are experimentally obtained.
 * possitive values get averaged more while negative values use a small
 * window so we can react faster to badness. */
267
268
#define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
#define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
269

270
/* BaseSink properties */
Wim Taymans's avatar
Wim Taymans committed
271

272
273
#define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
#define DEFAULT_CAN_ACTIVATE_PUSH TRUE
Wim Taymans's avatar
Wim Taymans committed
274

Wim Taymans's avatar
Wim Taymans committed
275
276
277
278
279
280
281
282
#define DEFAULT_PREROLL_QUEUE_LEN   0
#define DEFAULT_SYNC                TRUE
#define DEFAULT_MAX_LATENESS        -1
#define DEFAULT_QOS                 FALSE
#define DEFAULT_ASYNC               TRUE
#define DEFAULT_TS_OFFSET           0
#define DEFAULT_BLOCKSIZE           4096
#define DEFAULT_RENDER_DELAY        0
283

Wim Taymans's avatar
Wim Taymans committed
284
285
286
enum
{
  PROP_0,
287
  PROP_PREROLL_QUEUE_LEN,
288
  PROP_SYNC,
289
  PROP_MAX_LATENESS,
290
  PROP_QOS,
291
  PROP_ASYNC,
292
293
  PROP_TS_OFFSET,
  PROP_LAST_BUFFER,
294
  PROP_BLOCKSIZE,
295
  PROP_RENDER_DELAY,
296
  PROP_LAST
Wim Taymans's avatar
Wim Taymans committed
297
298
};

Wim Taymans's avatar
Wim Taymans committed
299
300
static GstElementClass *parent_class = NULL;

301
302
303
static void gst_base_sink_class_init (GstBaseSinkClass * klass);
static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);
static void gst_base_sink_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
304
305

GType
306
gst_base_sink_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
307
{
308
  static volatile gsize base_sink_type = 0;
Wim Taymans's avatar
Wim Taymans committed
309

310
311
  if (g_once_init_enter (&base_sink_type)) {
    GType _type;
312
    static const GTypeInfo base_sink_info = {
Wim Taymans's avatar
Wim Taymans committed
313
      sizeof (GstBaseSinkClass),
314
      NULL,
Wim Taymans's avatar
Wim Taymans committed
315
      NULL,
316
      (GClassInitFunc) gst_base_sink_class_init,
Wim Taymans's avatar
Wim Taymans committed
317
318
319
320
      NULL,
      NULL,
      sizeof (GstBaseSink),
      0,
321
      (GInstanceInitFunc) gst_base_sink_init,
Wim Taymans's avatar
Wim Taymans committed
322
    };
Wim Taymans's avatar
Wim Taymans committed
323

324
    _type = g_type_register_static (GST_TYPE_ELEMENT,
325
        "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
326
    g_once_init_leave (&base_sink_type, _type);
Wim Taymans's avatar
Wim Taymans committed
327
  }
328
  return base_sink_type;
Wim Taymans's avatar
Wim Taymans committed
329
}
Wim Taymans's avatar
Wim Taymans committed
330

331
static void gst_base_sink_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
332
    const GValue * value, GParamSpec * pspec);
333
static void gst_base_sink_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
334
335
    GValue * value, GParamSpec * pspec);

336
337
338
339
static gboolean gst_base_sink_send_event (GstElement * element,
    GstEvent * event);
static gboolean gst_base_sink_query (GstElement * element, GstQuery * query);

Wim Taymans's avatar
Wim Taymans committed
340
341
static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
342
343
static GstFlowReturn gst_base_sink_buffer_alloc (GstBaseSink * sink,
    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
344
static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
345
    GstClockTime * start, GstClockTime * end);
346
347
static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
    GstPad * pad, gboolean flushing);
348
static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
349
    gboolean active);
350
351
352
353
static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink,
    GstSegment * segment);
static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
    GstEvent * event, GstSegment * segment);
Wim Taymans's avatar
Wim Taymans committed
354

355
356
static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
    GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
357

358
static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
359
360
361
static GstFlowReturn gst_base_sink_chain_list (GstPad * pad,
    GstBufferList * list);

362
static void gst_base_sink_loop (GstPad * pad);
363
364
365
static gboolean gst_base_sink_pad_activate (GstPad * pad);
static gboolean gst_base_sink_pad_activate_push (GstPad * pad, gboolean active);
static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);
366
static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
367
static gboolean gst_base_sink_peer_query (GstBaseSink * sink, GstQuery * query);
Wim Taymans's avatar
Wim Taymans committed
368

369
static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
370
371
372
373
374
375
static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad);
static gboolean gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps);
static void gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps);
static GstFlowReturn gst_base_sink_pad_buffer_alloc (GstPad * pad,
    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);

376

377
378
379
/* check if an object was too late */
static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
    GstMiniObject * obj, GstClockTime start, GstClockTime stop,
380
    GstClockReturn status, GstClockTimeDiff jitter);
381
static GstFlowReturn gst_base_sink_preroll_object (GstBaseSink * basesink,
382
    gboolean is_list, GstMiniObject * obj);
383

Wim Taymans's avatar
Wim Taymans committed
384
static void
385
gst_base_sink_class_init (GstBaseSinkClass * klass)
Wim Taymans's avatar
Wim Taymans committed
386
387
388
389
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

390
391
  gobject_class = G_OBJECT_CLASS (klass);
  gstelement_class = GST_ELEMENT_CLASS (klass);
Wim Taymans's avatar
Wim Taymans committed
392

393
394
395
  GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
      "basesink element");

396
397
  g_type_class_add_private (klass, sizeof (GstBaseSinkPrivate));

398
  parent_class = g_type_class_peek_parent (klass);
Wim Taymans's avatar
Wim Taymans committed
399

400
401
402
  gobject_class->finalize = gst_base_sink_finalize;
  gobject_class->set_property = gst_base_sink_set_property;
  gobject_class->get_property = gst_base_sink_get_property;
Wim Taymans's avatar
Wim Taymans committed
403

Wim Taymans's avatar
Wim Taymans committed
404
  /* FIXME, this next value should be configured using an event from the
405
   * upstream element, ie, the BUFFER_SIZE event. */
406
  g_object_class_install_property (gobject_class, PROP_PREROLL_QUEUE_LEN,
407
408
      g_param_spec_uint ("preroll-queue-len", "Preroll queue length",
          "Number of buffers to queue during preroll", 0, G_MAXUINT,
409
410
          DEFAULT_PREROLL_QUEUE_LEN,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
411

412
  g_object_class_install_property (gobject_class, PROP_SYNC,
413
      g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
414
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
415

416
  g_object_class_install_property (gobject_class, PROP_MAX_LATENESS,
417
418
419
      g_param_spec_int64 ("max-lateness", "Max Lateness",
          "Maximum number of nanoseconds that a buffer can be late before it "
          "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS,
420
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
421

422
  g_object_class_install_property (gobject_class, PROP_QOS,
423
424
      g_param_spec_boolean ("qos", "Qos",
          "Generate Quality-of-Service events upstream", DEFAULT_QOS,
425
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
426
427
428
429
430
431
432
433
434
435
436
437
  /**
   * GstBaseSink:async
   *
   * If set to #TRUE, the basesink will perform asynchronous state changes.
   * When set to #FALSE, the sink will not signal the parent when it prerolls.
   * Use this option when dealing with sparse streams or when synchronisation is
   * not required.
   *
   * Since: 0.10.15
   */
  g_object_class_install_property (gobject_class, PROP_ASYNC,
      g_param_spec_boolean ("async", "Async",
438
439
          "Go asynchronously to PAUSED", DEFAULT_ASYNC,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
440
441
442
443
  /**
   * GstBaseSink:ts-offset
   *
   * Controls the final synchronisation, a negative value will render the buffer
Wim Taymans's avatar
Wim Taymans committed
444
   * earlier while a positive value delays playback. This property can be
445
446
447
448
449
450
451
   * used to fix synchronisation in bad files.
   *
   * Since: 0.10.15
   */
  g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
      g_param_spec_int64 ("ts-offset", "TS Offset",
          "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64,
452
          DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453
454
455
456
  /**
   * GstBaseSink:last-buffer
   *
   * The last buffer that arrived in the sink and was used for preroll or for
457
458
   * rendering. This property can be used to generate thumbnails. This property
   * can be NULL when the sink has not yet received a bufer.
459
460
461
462
463
464
   *
   * Since: 0.10.15
   */
  g_object_class_install_property (gobject_class, PROP_LAST_BUFFER,
      gst_param_spec_mini_object ("last-buffer", "Last Buffer",
          "The last buffer received in the sink", GST_TYPE_BUFFER,
465
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
466
467
468
469
470
471
472
473
474
475
476
  /**
   * GstBaseSink:blocksize
   *
   * The amount of bytes to pull when operating in pull mode.
   *
   * Since: 0.10.22
   */
  g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
      g_param_spec_uint ("blocksize", "Block size",
          "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
          DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
477
478
479
480
481
482
483
484
485
486
487
488
489
  /**
   * GstBaseSink:render-delay
   *
   * The additional delay between synchronisation and actual rendering of the
   * media. This property will add additional latency to the device in order to
   * make other sinks compensate for the delay.
   *
   * Since: 0.10.22
   */
  g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
      g_param_spec_uint64 ("render-delay", "Render Delay",
          "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64,
          DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
490

Wim Taymans's avatar
Wim Taymans committed
491
  gstelement_class->change_state =
492
      GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
493
494
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_base_sink_query);
Wim Taymans's avatar
Wim Taymans committed
495
496
497
498

  klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
  klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
  klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc);
499
  klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
500
501
  klass->activate_pull =
      GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
502
503
504
505
506
507
508
509
510
511
512
513

  /* Registering debug symbols for function pointers */
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_getcaps);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_setcaps);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_fixate);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_buffer_alloc);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_push);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_pull);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain);
  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list);
Wim Taymans's avatar
Wim Taymans committed
514
515
516
}

static GstCaps *
517
gst_base_sink_pad_getcaps (GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
518
519
520
521
522
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
  GstCaps *caps = NULL;

523
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
524
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
Wim Taymans's avatar
Wim Taymans committed
525

526
527
528
529
530
531
532
  if (bsink->pad_mode == GST_ACTIVATE_PULL) {
    /* if we are operating in pull mode we only accept the negotiated caps */
    GST_OBJECT_LOCK (pad);
    if ((caps = GST_PAD_CAPS (pad)))
      gst_caps_ref (caps);
    GST_OBJECT_UNLOCK (pad);
  }
Wim Taymans's avatar
Wim Taymans committed
533
  if (caps == NULL) {
534
535
    if (bclass->get_caps)
      caps = bclass->get_caps (bsink);
Wim Taymans's avatar
Wim Taymans committed
536

537
538
539
540
541
542
543
544
545
    if (caps == NULL) {
      GstPadTemplate *pad_template;

      pad_template =
          gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass),
          "sink");
      if (pad_template != NULL) {
        caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
      }
Wim Taymans's avatar
Wim Taymans committed
546
    }
Wim Taymans's avatar
Wim Taymans committed
547
  }
548
  gst_object_unref (bsink);
Wim Taymans's avatar
Wim Taymans committed
549

Wim Taymans's avatar
Wim Taymans committed
550
551
552
553
  return caps;
}

static gboolean
554
gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
555
556
557
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
558
  gboolean res = TRUE;
Wim Taymans's avatar
Wim Taymans committed
559

560
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
561
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
562

563
  if (res && bclass->set_caps)
Wim Taymans's avatar
Wim Taymans committed
564
565
    res = bclass->set_caps (bsink, caps);

566
567
  gst_object_unref (bsink);

Wim Taymans's avatar
Wim Taymans committed
568
569
570
  return res;
}

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
static void
gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps)
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;

  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
  bclass = GST_BASE_SINK_GET_CLASS (bsink);

  if (bclass->fixate)
    bclass->fixate (bsink, caps);

  gst_object_unref (bsink);
}

586
static GstFlowReturn
587
gst_base_sink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
588
    GstCaps * caps, GstBuffer ** buf)
Wim Taymans's avatar
Wim Taymans committed
589
590
591
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
592
  GstFlowReturn result = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
593

594
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
595
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
596

Wim Taymans's avatar
Wim Taymans committed
597
  if (bclass->buffer_alloc)
598
599
    result = bclass->buffer_alloc (bsink, offset, size, caps, buf);
  else
600
    *buf = NULL;                /* fallback in gstpad.c will allocate generic buffer */
Wim Taymans's avatar
Wim Taymans committed
601

602
603
  gst_object_unref (bsink);

604
  return result;
Wim Taymans's avatar
Wim Taymans committed
605
606
607
}

static void
608
gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
Wim Taymans's avatar
Wim Taymans committed
609
{
Wim Taymans's avatar
Wim Taymans committed
610
  GstPadTemplate *pad_template;
611
612
613
  GstBaseSinkPrivate *priv;

  basesink->priv = priv = GST_BASE_SINK_GET_PRIVATE (basesink);
Wim Taymans's avatar
Wim Taymans committed
614
615
616
617

  pad_template =
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
  g_return_if_fail (pad_template != NULL);
Wim Taymans's avatar
Wim Taymans committed
618

Wim Taymans's avatar
Wim Taymans committed
619
  basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
Wim Taymans's avatar
Wim Taymans committed
620

621
622
623
  gst_pad_set_getcaps_function (basesink->sinkpad, gst_base_sink_pad_getcaps);
  gst_pad_set_setcaps_function (basesink->sinkpad, gst_base_sink_pad_setcaps);
  gst_pad_set_fixatecaps_function (basesink->sinkpad, gst_base_sink_pad_fixate);
Wim Taymans's avatar
Wim Taymans committed
624
  gst_pad_set_bufferalloc_function (basesink->sinkpad,
625
626
      gst_base_sink_pad_buffer_alloc);
  gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate);
627
  gst_pad_set_activatepush_function (basesink->sinkpad,
628
      gst_base_sink_pad_activate_push);
629
  gst_pad_set_activatepull_function (basesink->sinkpad,
630
631
632
633
      gst_base_sink_pad_activate_pull);
  gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event);
  gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain);
  gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list);
634
  gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
635
636

  basesink->pad_mode = GST_ACTIVATE_NONE;
637
  basesink->preroll_queue = g_queue_new ();
638
  basesink->abidata.ABI.clip_segment = gst_segment_new ();
639
  priv->have_latency = FALSE;
640

641
642
643
  basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
  basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;

644
  basesink->sync = DEFAULT_SYNC;
645
  basesink->abidata.ABI.max_lateness = DEFAULT_MAX_LATENESS;
646
  g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS);
647
  priv->async_enabled = DEFAULT_ASYNC;
648
  priv->ts_offset = DEFAULT_TS_OFFSET;
649
  priv->render_delay = DEFAULT_RENDER_DELAY;
650
  priv->blocksize = DEFAULT_BLOCKSIZE;
651

652
  GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_IS_SINK);
Wim Taymans's avatar
Wim Taymans committed
653
654
}

655
static void
656
gst_base_sink_finalize (GObject * object)
657
658
659
{
  GstBaseSink *basesink;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
660
  basesink = GST_BASE_SINK (object);
661
662

  g_queue_free (basesink->preroll_queue);
663
  gst_segment_free (basesink->abidata.ABI.clip_segment);
664
665
666
667

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
/**
 * gst_base_sink_set_sync:
 * @sink: the sink
 * @sync: the new sync value.
 *
 * Configures @sink to synchronize on the clock or not. When
 * @sync is FALSE, incomming samples will be played as fast as
 * possible. If @sync is TRUE, the timestamps of the incomming
 * buffers will be used to schedule the exact render time of its
 * contents.
 *
 * Since: 0.10.4
 */
void
gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
{
684
685
  g_return_if_fail (GST_IS_BASE_SINK (sink));

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
  GST_OBJECT_LOCK (sink);
  sink->sync = sync;
  GST_OBJECT_UNLOCK (sink);
}

/**
 * gst_base_sink_get_sync:
 * @sink: the sink
 *
 * Checks if @sink is currently configured to synchronize against the
 * clock.
 *
 * Returns: TRUE if the sink is configured to synchronize against the clock.
 *
 * Since: 0.10.4
 */
gboolean
gst_base_sink_get_sync (GstBaseSink * sink)
{
  gboolean res;

707
708
  g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
  GST_OBJECT_LOCK (sink);
  res = sink->sync;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

/**
 * gst_base_sink_set_max_lateness:
 * @sink: the sink
 * @max_lateness: the new max lateness value.
 *
 * Sets the new max lateness value to @max_lateness. This value is
 * used to decide if a buffer should be dropped or not based on the
 * buffer timestamp and the current clock time. A value of -1 means
 * an unlimited time.
 *
 * Since: 0.10.4
 */
void
gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
{
731
732
  g_return_if_fail (GST_IS_BASE_SINK (sink));

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
  GST_OBJECT_LOCK (sink);
  sink->abidata.ABI.max_lateness = max_lateness;
  GST_OBJECT_UNLOCK (sink);
}

/**
 * gst_base_sink_get_max_lateness:
 * @sink: the sink
 *
 * Gets the max lateness value. See gst_base_sink_set_max_lateness for
 * more details.
 *
 * Returns: The maximum time in nanoseconds that a buffer can be late
 * before it is dropped and not rendered. A value of -1 means an
 * unlimited time.
 *
 * Since: 0.10.4
 */
gint64
gst_base_sink_get_max_lateness (GstBaseSink * sink)
{
  gint64 res;

756
757
  g_return_val_if_fail (GST_IS_BASE_SINK (sink), -1);

758
759
760
761
762
763
764
  GST_OBJECT_LOCK (sink);
  res = sink->abidata.ABI.max_lateness;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

765
766
767
/**
 * gst_base_sink_set_qos_enabled:
 * @sink: the sink
768
 * @enabled: the new qos value.
769
 *
770
 * Configures @sink to send Quality-of-Service events upstream.
771
772
773
774
775
776
 *
 * Since: 0.10.5
 */
void
gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
{
777
778
  g_return_if_fail (GST_IS_BASE_SINK (sink));

779
  g_atomic_int_set (&sink->priv->qos_enabled, enabled);
780
781
782
783
784
785
}

/**
 * gst_base_sink_is_qos_enabled:
 * @sink: the sink
 *
786
 * Checks if @sink is currently configured to send Quality-of-Service events
787
788
 * upstream.
 *
789
 * Returns: TRUE if the sink is configured to perform Quality-of-Service.
790
791
792
793
794
795
796
797
 *
 * Since: 0.10.5
 */
gboolean
gst_base_sink_is_qos_enabled (GstBaseSink * sink)
{
  gboolean res;

798
799
  g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);

800
  res = g_atomic_int_get (&sink->priv->qos_enabled);
801
802
803
804

  return res;
}

805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
/**
 * gst_base_sink_set_async_enabled:
 * @sink: the sink
 * @enabled: the new async value.
 *
 * Configures @sink to perform all state changes asynchronusly. When async is
 * disabled, the sink will immediatly go to PAUSED instead of waiting for a
 * preroll buffer. This feature is usefull if the sink does not synchronize
 * against the clock or when it is dealing with sparse streams.
 *
 * Since: 0.10.15
 */
void
gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
{
  g_return_if_fail (GST_IS_BASE_SINK (sink));

  GST_PAD_PREROLL_LOCK (sink->sinkpad);
  sink->priv->async_enabled = enabled;
824
  GST_LOG_OBJECT (sink, "set async enabled to %d", enabled);
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
  GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
}

/**
 * gst_base_sink_is_async_enabled:
 * @sink: the sink
 *
 * Checks if @sink is currently configured to perform asynchronous state
 * changes to PAUSED.
 *
 * Returns: TRUE if the sink is configured to perform asynchronous state
 * changes.
 *
 * Since: 0.10.15
 */
gboolean
gst_base_sink_is_async_enabled (GstBaseSink * sink)
{
  gboolean res;

  g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);

  GST_PAD_PREROLL_LOCK (sink->sinkpad);
  res = sink->priv->async_enabled;
  GST_PAD_PREROLL_UNLOCK (sink->sinkpad);

  return res;
}

854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
/**
 * gst_base_sink_set_ts_offset:
 * @sink: the sink
 * @offset: the new offset
 *
 * Adjust the synchronisation of @sink with @offset. A negative value will
 * render buffers earlier than their timestamp. A positive value will delay
 * rendering. This function can be used to fix playback of badly timestamped
 * buffers.
 *
 * Since: 0.10.15
 */
void
gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
{
  g_return_if_fail (GST_IS_BASE_SINK (sink));

  GST_OBJECT_LOCK (sink);
  sink->priv->ts_offset = offset;
873
  GST_LOG_OBJECT (sink, "set time offset to %" G_GINT64_FORMAT, offset);
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  GST_OBJECT_UNLOCK (sink);
}

/**
 * gst_base_sink_get_ts_offset:
 * @sink: the sink
 *
 * Get the synchronisation offset of @sink.
 *
 * Returns: The synchronisation offset.
 *
 * Since: 0.10.15
 */
GstClockTimeDiff
gst_base_sink_get_ts_offset (GstBaseSink * sink)
{
  GstClockTimeDiff res;

  g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);

  GST_OBJECT_LOCK (sink);
  res = sink->priv->ts_offset;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

901
902
903
904
905
906
907
908
/**
 * gst_base_sink_get_last_buffer:
 * @sink: the sink
 *
 * Get the last buffer that arrived in the sink and was used for preroll or for
 * rendering. This property can be used to generate thumbnails.
 *
 * The #GstCaps on the buffer can be used to determine the type of the buffer.
Wim Taymans's avatar
Wim Taymans committed
909
 *
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
 * Returns: a #GstBuffer. gst_buffer_unref() after usage. This function returns
 * NULL when no buffer has arrived in the sink yet or when the sink is not in
 * PAUSED or PLAYING.
 *
 * Since: 0.10.15
 */
GstBuffer *
gst_base_sink_get_last_buffer (GstBaseSink * sink)
{
  GstBuffer *res;

  g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);

  GST_OBJECT_LOCK (sink);
  if ((res = sink->priv->last_buffer))
    gst_buffer_ref (res);
  GST_OBJECT_UNLOCK (sink);

  return res;
}

static void
gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
{
934
935
  GstBuffer *old;

936
  GST_OBJECT_LOCK (sink);
937
  old = sink->priv->last_buffer;
938
939
940
941
942
943
944
945
  if (G_LIKELY (old != buffer)) {
    GST_DEBUG_OBJECT (sink, "setting last buffer to %p", buffer);
    if (G_LIKELY (buffer))
      gst_buffer_ref (buffer);
    sink->priv->last_buffer = buffer;
  } else {
    old = NULL;
  }
946
  GST_OBJECT_UNLOCK (sink);
947

948
949
950
  /* avoid unreffing with the lock because cleanup code might want to take the
   * lock too */
  if (G_LIKELY (old))
951
    gst_buffer_unref (old);
952
953
}

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
/**
 * gst_base_sink_get_latency:
 * @sink: the sink
 *
 * Get the currently configured latency.
 *
 * Returns: The configured latency.
 *
 * Since: 0.10.12
 */
GstClockTime
gst_base_sink_get_latency (GstBaseSink * sink)
{
  GstClockTime res;

  GST_OBJECT_LOCK (sink);
  res = sink->priv->latency;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

/**
 * gst_base_sink_query_latency:
 * @sink: the sink
 * @live: if the sink is live
 * @upstream_live: if an upstream element is live
 * @min_latency: the min latency of the upstream elements
 * @max_latency: the max latency of the upstream elements
 *
 * Query the sink for the latency parameters. The latency will be queried from
 * the upstream elements. @live will be TRUE if @sink is configured to
 * synchronize against the clock. @upstream_live will be TRUE if an upstream
Wim Taymans's avatar
Wim Taymans committed
987
 * element is live.
988
989
990
991
992
 *
 * If both @live and @upstream_live are TRUE, the sink will want to compensate
 * for the latency introduced by the upstream elements by setting the
 * @min_latency to a strictly possitive value.
 *
Wim Taymans's avatar
Wim Taymans committed
993
 * This function is mostly used by subclasses.
994
995
996
997
998
999
1000
1001
1002
1003
 *
 * Returns: TRUE if the query succeeded.
 *
 * Since: 0.10.12
 */
gboolean
gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
    gboolean * upstream_live, GstClockTime * min_latency,
    GstClockTime * max_latency)
{
1004
  gboolean l, us_live, res, have_latency;
1005
  GstClockTime min, max, render_delay;
1006
  GstQuery *query;
1007
  GstClockTime us_min, us_max;
1008
1009

  /* we are live when we sync to the clock */
1010
1011
1012
  GST_OBJECT_LOCK (sink);
  l = sink->sync;
  have_latency = sink->priv->have_latency;
1013
  render_delay = sink->priv->render_delay;
1014
  GST_OBJECT_UNLOCK (sink);
1015
1016
1017
1018
1019

  /* assume no latency */
  min = 0;
  max = -1;
  us_live = FALSE;
1020

1021
1022
1023
1024
1025
1026
1027
1028
  if (have_latency) {
    GST_DEBUG_OBJECT (sink, "we are ready for LATENCY query");
    /* we are ready for a latency query this is when we preroll or when we are
     * not async. */
    query = gst_query_new_latency ();

    /* ask the peer for the latency */
    if ((res = gst_base_sink_peer_query (sink, query))) {
1029
1030
1031
1032
1033
1034
1035
1036
1037
      /* get upstream min and max latency */
      gst_query_parse_latency (query, &us_live, &us_min, &us_max);

      if (us_live) {
        /* upstream live, use its latency, subclasses should use these
         * values to create the complete latency. */
        min = us_min;
        max = us_max;
      }
1038
1039
1040
1041
1042
1043
1044
      if (l) {
        /* we need to add the render delay if we are live */
        if (min != -1)
          min += render_delay;
        if (max != -1)
          max += render_delay;
      }
1045
    }
1046
    gst_query_unref (query);
1047
  } else {
1048
1049
    GST_DEBUG_OBJECT (sink, "we are not yet ready for LATENCY query");
    res = FALSE;
1050
1051
  }

1052
1053
1054
1055
1056
1057
1058
1059
1060
  /* not live, we tried to do the query, if it failed we return TRUE anyway */
  if (!res) {
    if (!l) {
      res = TRUE;
      GST_DEBUG_OBJECT (sink, "latency query failed but we are not live");
    } else {
      GST_DEBUG_OBJECT (sink, "latency query failed and we are live");
    }
  }
1061

1062
1063
1064
1065
  if (res) {
    GST_DEBUG_OBJECT (sink, "latency query: live: %d, have_latency %d,"
        " upstream: %d, min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, l,
        have_latency, us_live, GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1066

1067
1068
1069
1070
1071
1072
1073
1074
    if (live)
      *live = l;
    if (upstream_live)
      *upstream_live = us_live;
    if (min_latency)
      *min_latency = min;
    if (max_latency)
      *max_latency = max;
1075
  }
1076
  return res;
1077
1078
}

1079
1080
1081
1082
1083
/**
 * gst_base_sink_set_render_delay:
 * @sink: a #GstBaseSink
 * @delay: the new delay
 *
Wim Taymans's avatar
Wim Taymans committed
1084
 * Set the render delay in @sink to @delay. The render delay is the time
1085
1086
 * between actual rendering of a buffer and its synchronisation time. Some
 * devices might delay media rendering which can be compensated for with this
Wim Taymans's avatar
Wim Taymans committed
1087
 * function.
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
 *
 * After calling this function, this sink will report additional latency and
 * other sinks will adjust their latency to delay the rendering of their media.
 *
 * This function is usually called by subclasses.
 *
 * Since: 0.10.21
 */
void
gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
{
1099
1100
  GstClockTime old_render_delay;

1101
1102
1103
  g_return_if_fail (GST_IS_BASE_SINK (sink));

  GST_OBJECT_LOCK (sink);
1104
  old_render_delay = sink->priv->render_delay;
1105
1106
1107
1108
  sink->priv->render_delay = delay;
  GST_LOG_OBJECT (sink, "set render delay to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (delay));
  GST_OBJECT_UNLOCK (sink);
1109
1110
1111
1112
1113
1114

  if (delay != old_render_delay) {
    GST_DEBUG_OBJECT (sink, "posting latency changed");
    gst_element_post_message (GST_ELEMENT_CAST (sink),
        gst_message_new_latency (GST_OBJECT_CAST (sink)));
  }
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
}

/**
 * gst_base_sink_get_render_delay:
 * @sink: a #GstBaseSink
 *
 * Get the render delay of @sink. see gst_base_sink_set_render_delay() for more
 * information about the render delay.
 *
 * Returns: the render delay of @sink.
 *
 * Since: 0.10.21
 */
GstClockTime
gst_base_sink_get_render_delay (GstBaseSink * sink)
{
  GstClockTimeDiff res;

  g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);

  GST_OBJECT_LOCK (sink);
  res = sink->priv->render_delay;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
/**
 * gst_base_sink_set_blocksize:
 * @sink: a #GstBaseSink
 * @blocksize: the blocksize in bytes
 *
 * Set the number of bytes that the sink will pull when it is operating in pull
 * mode.
 *
 * Since: 0.10.22
 */
void
gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
{
  g_return_if_fail (GST_IS_BASE_SINK (sink));

  GST_OBJECT_LOCK (sink);
  sink->priv->blocksize = blocksize;
  GST_LOG_OBJECT (sink, "set blocksize to %u", blocksize);
  GST_OBJECT_UNLOCK (sink);
}

/**
 * gst_base_sink_get_blocksize:
 * @sink: a #GstBaseSink
 *
 * Get the number of bytes that the sink will pull when it is operating in pull
 * mode.
 *
 * Returns: the number of bytes @sink will pull in pull mode.
 *
 * Since: 0.10.22
 */
guint
gst_base_sink_get_blocksize (GstBaseSink * sink)
{
  guint res;

  g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);

  GST_OBJECT_LOCK (sink);
  res = sink->priv->blocksize;
  GST_OBJECT_UNLOCK (sink);

  return res;
}

Wim Taymans's avatar
Wim Taymans committed
1188
static void
1189
gst_base_sink_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
1190
1191
    const GValue * value, GParamSpec * pspec)
{
1192
  GstBaseSink *sink = GST_BASE_SINK (object);
Wim Taymans's avatar
Wim Taymans committed