gstappsink.c 33.3 KB
Newer Older
David Schleef's avatar
David Schleef committed
1
2
/* GStreamer
 * Copyright (C) 2007 David Schleef <ds@schleef.org>
3
 *           (C) 2008 Wim Taymans <wim.taymans@gmail.com>
David Schleef's avatar
David Schleef committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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.
 */

21
22
/**
 * SECTION:element-appsink
23
24
25
26
27
28
29
30
31
 * 
 * Appsink is a sink plugin that supports many different methods for making
 * the application get a handle on the GStreamer data in a pipeline. Unlike
 * most GStreamer elements, Appsink provides external API functions.
 *
 * For the documentation of the API, please see the
 * <link linkend="gst-plugins-base-libs-appsink">libgstapp</link> section in
 * the GStreamer Plugins Base Libraries documentation.
 *
32
 * Since: 0.10.22
33
34
35
36
37
 */


/**
 * SECTION:gstappsink
38
 * @see_also: #GstBaseSink, appsrc
39
40
 *
 * Appsink is a sink plugin that supports many different methods for making
41
42
 * the application get a handle on the GStreamer data in a pipeline. Unlike
 * most GStreamer elements, Appsink provides external API functions.
43
 *
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
 * appsink can be used by linking to the gstappsink.h header file to access the
 * methods or by using the appsink action signals and properties.
 *
 * The normal way of retrieving buffers from appsink is by using the
 * gst_app_sink_pull_buffer() and gst_app_sink_pull_preroll() methods.
 * These methods block until a buffer becomes available in the sink or when the
 * sink is shut down or reaches EOS.
 *
 * Appsink will internally use a queue to collect buffers from the streaming
 * thread. If the application is not pulling buffers fast enough, this queue
 * will consume a lot of memory over time. The "max-buffers" property can be
 * used to limit the queue size. The "drop" property controls whether the
 * streaming thread blocks or if older buffers are dropped when the maximum
 * queue size is reached. Note that blocking the streaming thread can negatively
 * affect real-time performance and should be avoided.
 *
 * If a blocking behaviour is not desirable, setting the "emit-signals" property
 * to %TRUE will make appsink emit the "new-buffer" and "new-preroll" signals
 * when a buffer can be pulled without blocking.
 *
 * The "caps" property on appsink can be used to control the formats that
 * appsink can receive. This property can contain non-fixed caps, the format of
 * the pulled buffers can be obtained by getting the buffer caps.
 *
 * If one of the pull-preroll or pull-buffer methods return %NULL, the appsink
 * is stopped or in the EOS state. You can check for the EOS state with the
 * "eos" property or with the gst_app_sink_is_eos() method.
 *
 * The eos signal can also be used to be informed when the EOS state is reached
 * to avoid polling.
 *
75
76
77
 * Since: 0.10.22
 *
 * Last reviewed on 2008-12-17 (0.10.22)
78
79
 */

David Schleef's avatar
David Schleef committed
80
81
82
83
84
85
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
86
#include <gst/gstbuffer.h>
David Schleef's avatar
David Schleef committed
87
88
89
90
91

#include <string.h>

#include "gstappsink.h"

92
93
94
95
96
97
98
99
100
101
102
103
104
105
struct _GstAppSinkPrivate
{
  GstCaps *caps;
  gboolean emit_signals;
  guint max_buffers;
  gboolean drop;

  GCond *cond;
  GMutex *mutex;
  GQueue *queue;
  GstBuffer *preroll;
  gboolean flushing;
  gboolean started;
  gboolean is_eos;
106
107
108
109

  GstAppSinkCallbacks callbacks;
  gpointer user_data;
  GDestroyNotify notify;
110
};
David Schleef's avatar
David Schleef committed
111

112
GST_DEBUG_CATEGORY_STATIC (app_sink_debug);
David Schleef's avatar
David Schleef committed
113
114
115
116
#define GST_CAT_DEFAULT app_sink_debug

enum
{
117
118
119
120
121
  /* signals */
  SIGNAL_EOS,
  SIGNAL_NEW_PREROLL,
  SIGNAL_NEW_BUFFER,

122
  /* actions */
123
124
125
126
127
128
  SIGNAL_PULL_PREROLL,
  SIGNAL_PULL_BUFFER,

  LAST_SIGNAL
};

129
#define DEFAULT_PROP_EOS		TRUE
130
#define DEFAULT_PROP_EMIT_SIGNALS	FALSE
131
#define DEFAULT_PROP_MAX_BUFFERS	0
132
#define DEFAULT_PROP_DROP		FALSE
133

134
135
136
137
enum
{
  PROP_0,
  PROP_CAPS,
138
139
140
  PROP_EOS,
  PROP_EMIT_SIGNALS,
  PROP_MAX_BUFFERS,
141
142
  PROP_DROP,
  PROP_LAST
David Schleef's avatar
David Schleef committed
143
144
145
146
147
148
149
150
};

static GstStaticPadTemplate gst_app_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);

151
152
153
static void gst_app_sink_dispose (GObject * object);
static void gst_app_sink_finalize (GObject * object);

David Schleef's avatar
David Schleef committed
154
155
156
157
static void gst_app_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_app_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
158

159
160
static gboolean gst_app_sink_unlock_start (GstBaseSink * bsink);
static gboolean gst_app_sink_unlock_stop (GstBaseSink * bsink);
David Schleef's avatar
David Schleef committed
161
162
163
static gboolean gst_app_sink_start (GstBaseSink * psink);
static gboolean gst_app_sink_stop (GstBaseSink * psink);
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event);
164
165
static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
    GstBuffer * buffer);
David Schleef's avatar
David Schleef committed
166
167
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
    GstBuffer * buffer);
168
169
170
static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink);

static guint gst_app_sink_signals[LAST_SIGNAL] = { 0 };
David Schleef's avatar
David Schleef committed
171
172
173

GST_BOILERPLATE (GstAppSink, gst_app_sink, GstBaseSink, GST_TYPE_BASE_SINK);

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
void
gst_app_marshal_OBJECT__VOID (GClosure * closure,
    GValue * return_value,
    guint n_param_values,
    const GValue * param_values,
    gpointer invocation_hint, gpointer marshal_data)
{
  typedef GstBuffer *(*GMarshalFunc_OBJECT__VOID) (gpointer data1,
      gpointer data2);
  register GMarshalFunc_OBJECT__VOID callback;
  register GCClosure *cc = (GCClosure *) closure;
  register gpointer data1, data2;
  GstBuffer *v_return;

  g_return_if_fail (return_value != NULL);
  g_return_if_fail (n_param_values == 1);

  if (G_CCLOSURE_SWAP_DATA (closure)) {
    data1 = closure->data;
    data2 = g_value_peek_pointer (param_values + 0);
  } else {
    data1 = g_value_peek_pointer (param_values + 0);
    data2 = closure->data;
  }
  callback =
      (GMarshalFunc_OBJECT__VOID) (marshal_data ? marshal_data : cc->callback);

  v_return = callback (data1, data2);

  gst_value_take_buffer (return_value, v_return);
}

David Schleef's avatar
David Schleef committed
206
207
208
209
210
211
212
static void
gst_app_sink_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  GST_DEBUG_CATEGORY_INIT (app_sink_debug, "appsink", 0, "appsink element");

213
214
215
  gst_element_class_set_details_simple (element_class, "AppSink",
      "Generic/Sink", "Allow the application to get access to raw buffer",
      "David Schleef <ds@schleef.org>, Wim Taymans <wim.taymans@gmail.com>");
David Schleef's avatar
David Schleef committed
216
217
218
219
220
221
222
223
224
225
226

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_app_sink_template));
}

static void
gst_app_sink_class_init (GstAppSinkClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;

227
228
229
  gobject_class->dispose = gst_app_sink_dispose;
  gobject_class->finalize = gst_app_sink_finalize;

David Schleef's avatar
David Schleef committed
230
231
  gobject_class->set_property = gst_app_sink_set_property;
  gobject_class->get_property = gst_app_sink_get_property;
232
233
234

  g_object_class_install_property (gobject_class, PROP_CAPS,
      g_param_spec_boxed ("caps", "Caps",
235
          "The allowed caps for the sink pad", GST_TYPE_CAPS,
236
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
237
238
239

  g_object_class_install_property (gobject_class, PROP_EOS,
      g_param_spec_boolean ("eos", "EOS",
240
241
242
243
244
245
246
247
248
249
          "Check if the sink is EOS or not started", DEFAULT_PROP_EOS,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_EMIT_SIGNALS,
      g_param_spec_boolean ("emit-signals", "Emit signals",
          "Emit new-preroll and new-buffer signals", DEFAULT_PROP_EMIT_SIGNALS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_MAX_BUFFERS,
      g_param_spec_uint ("max-buffers", "Max Buffers",
250
          "The maximum number of buffers to queue internally (0 = unlimited)",
251
252
          0, G_MAXUINT, DEFAULT_PROP_MAX_BUFFERS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
253

254
255
256
257
258
  g_object_class_install_property (gobject_class, PROP_DROP,
      g_param_spec_boolean ("drop", "Drop",
          "Drop old buffers when the buffer queue is filled", DEFAULT_PROP_DROP,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

259
260
261
262
  /**
   * GstAppSink::eos:
   * @appsink: the appsink element that emited the signal
   *
263
264
   * Signal that the end-of-stream has been reached. This signal is emited from
   * the steaming thread.
265
266
267
268
269
270
271
272
273
   */
  gst_app_sink_signals[SIGNAL_EOS] =
      g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstAppSinkClass, eos),
      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
  /**
   * GstAppSink::new-preroll:
   * @appsink: the appsink element that emited the signal
   *
274
275
276
277
278
279
280
281
282
283
284
   * Signal that a new preroll buffer is available. 
   *
   * This signal is emited from the steaming thread and only when the
   * "emit-signals" property is %TRUE. 
   *
   * The new preroll buffer can be retrieved with the "pull-preroll" action
   * signal or gst_app_sink_pull_preroll() either from this signal callback
   * or from any other thread.
   *
   * Note that this signal is only emited when the "emit-signals" property is
   * set to %TRUE, which it is not by default for performance reasons.
285
286
287
288
   */
  gst_app_sink_signals[SIGNAL_NEW_PREROLL] =
      g_signal_new ("new-preroll", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstAppSinkClass, new_preroll),
289
      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
290
291
292
293
294
  /**
   * GstAppSink::new-buffer:
   * @appsink: the appsink element that emited the signal
   *
   * Signal that a new buffer is available.
295
296
297
298
   *
   * This signal is emited from the steaming thread and only when the
   * "emit-signals" property is %TRUE. 
   *
299
   * The new buffer can be retrieved with the "pull-buffer" action
300
301
302
303
304
   * signal or gst_app_sink_pull_buffer() either from this signal callback
   * or from any other thread.
   *
   * Note that this signal is only emited when the "emit-signals" property is
   * set to %TRUE, which it is not by default for performance reasons.
305
306
307
308
   */
  gst_app_sink_signals[SIGNAL_NEW_BUFFER] =
      g_signal_new ("new-buffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstAppSinkClass, new_buffer),
309
      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
310
311
312
313
314

  /**
   * GstAppSink::pull-preroll:
   * @appsink: the appsink element to emit this signal on
   *
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
   * Get the last preroll buffer in @appsink. This was the buffer that caused the
   * appsink to preroll in the PAUSED state. This buffer can be pulled many times
   * and remains available to the application even after EOS.
   *
   * This function is typically used when dealing with a pipeline in the PAUSED
   * state. Calling this function after doing a seek will give the buffer right
   * after the seek position.
   *
   * Note that the preroll buffer will also be returned as the first buffer
   * when calling gst_app_sink_pull_buffer() or the "pull-buffer" action signal.
   *
   * If an EOS event was received before any buffers, this function returns
   * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. 
   *
   * This function blocks until a preroll buffer or EOS is received or the appsink
   * element is set to the READY/NULL state. 
   *
   * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
333
334
335
   */
  gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
      g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass),
336
337
338
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
          pull_preroll), NULL, NULL, gst_app_marshal_OBJECT__VOID,
      GST_TYPE_BUFFER, 0, G_TYPE_NONE);
339
340
341
342
  /**
   * GstAppSink::pull-buffer:
   * @appsink: the appsink element to emit this signal on
   *
343
344
345
346
347
   * This function blocks until a buffer or EOS becomes available or the appsink
   * element is set to the READY/NULL state. 
   *
   * This function will only return buffers when the appsink is in the PLAYING
   * state. All rendered buffers will be put in a queue so that the application
348
349
350
351
352
353
   * can pull buffers at its own rate. 
   *
   * Note that when the application does not pull buffers fast enough, the
   * queued buffers could consume a lot of memory, especially when dealing with
   * raw video frames. It's possible to control the behaviour of the queue with
   * the "drop" and "max-buffers" properties.
354
355
356
357
358
   *
   * If an EOS event was received before any buffers, this function returns
   * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. 
   *
   * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
359
   */
360
  gst_app_sink_signals[SIGNAL_PULL_BUFFER] =
361
362
363
364
      g_signal_new ("pull-buffer", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
          pull_buffer), NULL, NULL, gst_app_marshal_OBJECT__VOID,
      GST_TYPE_BUFFER, 0, G_TYPE_NONE);
David Schleef's avatar
David Schleef committed
365

366
367
  basesink_class->unlock = gst_app_sink_unlock_start;
  basesink_class->unlock_stop = gst_app_sink_unlock_stop;
David Schleef's avatar
David Schleef committed
368
369
370
  basesink_class->start = gst_app_sink_start;
  basesink_class->stop = gst_app_sink_stop;
  basesink_class->event = gst_app_sink_event;
371
  basesink_class->preroll = gst_app_sink_preroll;
David Schleef's avatar
David Schleef committed
372
  basesink_class->render = gst_app_sink_render;
373
374
375
376
  basesink_class->get_caps = gst_app_sink_getcaps;

  klass->pull_preroll = gst_app_sink_pull_preroll;
  klass->pull_buffer = gst_app_sink_pull_buffer;
377
378

  g_type_class_add_private (klass, sizeof (GstAppSinkPrivate));
379
380
381
382
383
}

static void
gst_app_sink_init (GstAppSink * appsink, GstAppSinkClass * klass)
{
384
385
  appsink->priv = G_TYPE_INSTANCE_GET_PRIVATE (appsink, GST_TYPE_APP_SINK,
      GstAppSinkPrivate);
386

387
388
389
390
391
392
393
  appsink->priv->mutex = g_mutex_new ();
  appsink->priv->cond = g_cond_new ();
  appsink->priv->queue = g_queue_new ();

  appsink->priv->emit_signals = DEFAULT_PROP_EMIT_SIGNALS;
  appsink->priv->max_buffers = DEFAULT_PROP_MAX_BUFFERS;
  appsink->priv->drop = DEFAULT_PROP_DROP;
David Schleef's avatar
David Schleef committed
394
395
396
397
398
399
}

static void
gst_app_sink_dispose (GObject * obj)
{
  GstAppSink *appsink = GST_APP_SINK (obj);
400
  GstBuffer *buffer;
David Schleef's avatar
David Schleef committed
401

402
  GST_OBJECT_LOCK (appsink);
403
404
405
  if (appsink->priv->caps) {
    gst_caps_unref (appsink->priv->caps);
    appsink->priv->caps = NULL;
David Schleef's avatar
David Schleef committed
406
  }
407
408
409
410
411
412
  if (appsink->priv->notify) {
    appsink->priv->notify (appsink->priv->user_data);
  }
  appsink->priv->user_data = NULL;
  appsink->priv->notify = NULL;

413
414
  GST_OBJECT_UNLOCK (appsink);

415
416
417
418
  g_mutex_lock (appsink->priv->mutex);
  if (appsink->priv->preroll) {
    gst_buffer_unref (appsink->priv->preroll);
    appsink->priv->preroll = NULL;
419
  }
420
  while ((buffer = g_queue_pop_head (appsink->priv->queue)))
421
    gst_buffer_unref (buffer);
422
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
423
424
425
426
427

  G_OBJECT_CLASS (parent_class)->dispose (obj);
}

static void
428
gst_app_sink_finalize (GObject * obj)
David Schleef's avatar
David Schleef committed
429
{
430
431
  GstAppSink *appsink = GST_APP_SINK (obj);

432
433
434
  g_mutex_free (appsink->priv->mutex);
  g_cond_free (appsink->priv->cond);
  g_queue_free (appsink->priv->queue);
435
436

  G_OBJECT_CLASS (parent_class)->finalize (obj);
David Schleef's avatar
David Schleef committed
437
438
439
440
441
442
443
444
445
}

static void
gst_app_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstAppSink *appsink = GST_APP_SINK (object);

  switch (prop_id) {
446
447
448
    case PROP_CAPS:
      gst_app_sink_set_caps (appsink, gst_value_get_caps (value));
      break;
449
450
451
452
453
454
    case PROP_EMIT_SIGNALS:
      gst_app_sink_set_emit_signals (appsink, g_value_get_boolean (value));
      break;
    case PROP_MAX_BUFFERS:
      gst_app_sink_set_max_buffers (appsink, g_value_get_uint (value));
      break;
455
456
457
    case PROP_DROP:
      gst_app_sink_set_drop (appsink, g_value_get_boolean (value));
      break;
David Schleef's avatar
David Schleef committed
458
459
460
461
462
463
464
465
466
467
468
469
470
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_app_sink_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstAppSink *appsink = GST_APP_SINK (object);

  switch (prop_id) {
471
472
473
474
475
476
477
478
479
480
481
482
483
    case PROP_CAPS:
    {
      GstCaps *caps;

      caps = gst_app_sink_get_caps (appsink);
      gst_value_set_caps (value, caps);
      if (caps)
        gst_caps_unref (caps);
      break;
    }
    case PROP_EOS:
      g_value_set_boolean (value, gst_app_sink_is_eos (appsink));
      break;
484
485
486
487
488
489
    case PROP_EMIT_SIGNALS:
      g_value_set_boolean (value, gst_app_sink_get_emit_signals (appsink));
      break;
    case PROP_MAX_BUFFERS:
      g_value_set_uint (value, gst_app_sink_get_max_buffers (appsink));
      break;
490
491
492
    case PROP_DROP:
      g_value_set_boolean (value, gst_app_sink_get_drop (appsink));
      break;
David Schleef's avatar
David Schleef committed
493
494
495
496
497
498
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

499
500
501
502
503
static gboolean
gst_app_sink_unlock_start (GstBaseSink * bsink)
{
  GstAppSink *appsink = GST_APP_SINK (bsink);

504
  g_mutex_lock (appsink->priv->mutex);
505
  GST_DEBUG_OBJECT (appsink, "unlock start");
506
507
508
  appsink->priv->flushing = TRUE;
  g_cond_signal (appsink->priv->cond);
  g_mutex_unlock (appsink->priv->mutex);
509
510
511
512
513
514
515
516
517

  return TRUE;
}

static gboolean
gst_app_sink_unlock_stop (GstBaseSink * bsink)
{
  GstAppSink *appsink = GST_APP_SINK (bsink);

518
  g_mutex_lock (appsink->priv->mutex);
519
  GST_DEBUG_OBJECT (appsink, "unlock stop");
520
521
522
  appsink->priv->flushing = FALSE;
  g_cond_signal (appsink->priv->cond);
  g_mutex_unlock (appsink->priv->mutex);
523
524
525
526

  return TRUE;
}

527
528
529
static void
gst_app_sink_flush_unlocked (GstAppSink * appsink)
{
530
531
  GstBuffer *buffer;

532
  GST_DEBUG_OBJECT (appsink, "flush stop appsink");
533
534
535
  appsink->priv->is_eos = FALSE;
  gst_buffer_replace (&appsink->priv->preroll, NULL);
  while ((buffer = g_queue_pop_head (appsink->priv->queue)))
536
    gst_buffer_unref (buffer);
537
  g_cond_signal (appsink->priv->cond);
538
539
}

David Schleef's avatar
David Schleef committed
540
541
542
543
544
static gboolean
gst_app_sink_start (GstBaseSink * psink)
{
  GstAppSink *appsink = GST_APP_SINK (psink);

545
  g_mutex_lock (appsink->priv->mutex);
546
  GST_DEBUG_OBJECT (appsink, "starting");
547
548
  appsink->priv->started = TRUE;
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
549
550
551
552
553
554
555

  return TRUE;
}

static gboolean
gst_app_sink_stop (GstBaseSink * psink)
{
556
557
  GstAppSink *appsink = GST_APP_SINK (psink);

558
  g_mutex_lock (appsink->priv->mutex);
559
  GST_DEBUG_OBJECT (appsink, "stopping");
560
561
  appsink->priv->flushing = TRUE;
  appsink->priv->started = FALSE;
562
  gst_app_sink_flush_unlocked (appsink);
563
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
564
565
566
567
568
569
570
571
572
573
574

  return TRUE;
}

static gboolean
gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
{
  GstAppSink *appsink = GST_APP_SINK (sink);

  switch (event->type) {
    case GST_EVENT_EOS:
575

576
      g_mutex_lock (appsink->priv->mutex);
577
      GST_DEBUG_OBJECT (appsink, "receiving EOS");
578
579
580
      appsink->priv->is_eos = TRUE;
      g_cond_signal (appsink->priv->cond);
      g_mutex_unlock (appsink->priv->mutex);
581
582
583

      /* emit EOS now */
      g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_EOS], 0);
584
585
586

      if (appsink->priv->callbacks.eos)
        appsink->priv->callbacks.eos (appsink, appsink->priv->user_data);
587
588
      break;
    case GST_EVENT_FLUSH_START:
589
590
591
      /* we don't have to do anything here, the base class will call unlock
       * which will make sure we exit the _render method */
      GST_DEBUG_OBJECT (appsink, "received FLUSH_START");
592
593
      break;
    case GST_EVENT_FLUSH_STOP:
594
      g_mutex_lock (appsink->priv->mutex);
595
596
      GST_DEBUG_OBJECT (appsink, "received FLUSH_STOP");
      gst_app_sink_flush_unlocked (appsink);
597
      g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
598
599
600
601
      break;
    default:
      break;
  }
602
603
  return TRUE;
}
David Schleef's avatar
David Schleef committed
604

605
606
607
608
static GstFlowReturn
gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer)
{
  GstAppSink *appsink = GST_APP_SINK (psink);
609
  gboolean emit;
610

611
612
  g_mutex_lock (appsink->priv->mutex);
  if (appsink->priv->flushing)
613
614
    goto flushing;

615
  GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer);
616
617
618
619
  gst_buffer_replace (&appsink->priv->preroll, buffer);
  g_cond_signal (appsink->priv->cond);
  emit = appsink->priv->emit_signals;
  g_mutex_unlock (appsink->priv->mutex);
620

621
622
623
  if (emit)
    g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_PREROLL], 0);

624
625
626
  if (appsink->priv->callbacks.new_preroll)
    appsink->priv->callbacks.new_preroll (appsink, appsink->priv->user_data);

627
  return GST_FLOW_OK;
628
629
630
631

flushing:
  {
    GST_DEBUG_OBJECT (appsink, "we are flushing");
632
    g_mutex_unlock (appsink->priv->mutex);
633
634
    return GST_FLOW_WRONG_STATE;
  }
David Schleef's avatar
David Schleef committed
635
636
637
638
639
640
}

static GstFlowReturn
gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer)
{
  GstAppSink *appsink = GST_APP_SINK (psink);
641
  gboolean emit;
David Schleef's avatar
David Schleef committed
642

643
644
  g_mutex_lock (appsink->priv->mutex);
  if (appsink->priv->flushing)
645
646
647
    goto flushing;

  GST_DEBUG_OBJECT (appsink, "pushing render buffer %p on queue (%d)",
648
      buffer, appsink->priv->queue->length);
649

650
651
652
  while (appsink->priv->max_buffers > 0 &&
      appsink->priv->queue->length >= appsink->priv->max_buffers) {
    if (appsink->priv->drop) {
653
654
655
      GstBuffer *buf;

      /* we need to drop the oldest buffer and try again */
656
      buf = g_queue_pop_head (appsink->priv->queue);
657
658
659
660
      GST_DEBUG_OBJECT (appsink, "dropping old buffer %p", buf);
      gst_buffer_unref (buf);
    } else {
      GST_DEBUG_OBJECT (appsink, "waiting for free space, length %d >= %d",
661
          appsink->priv->queue->length, appsink->priv->max_buffers);
662
      /* wait for a buffer to be removed or flush */
663
664
      g_cond_wait (appsink->priv->cond, appsink->priv->mutex);
      if (appsink->priv->flushing)
665
666
        goto flushing;
    }
667
  }
668
  /* we need to ref the buffer when pushing it in the queue */
669
670
671
672
  g_queue_push_tail (appsink->priv->queue, gst_buffer_ref (buffer));
  g_cond_signal (appsink->priv->cond);
  emit = appsink->priv->emit_signals;
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
673

674
675
676
  if (emit)
    g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_BUFFER], 0);

677
678
679
  if (appsink->priv->callbacks.new_buffer)
    appsink->priv->callbacks.new_buffer (appsink, appsink->priv->user_data);

David Schleef's avatar
David Schleef committed
680
  return GST_FLOW_OK;
681

682
flushing:
683
  {
684
    GST_DEBUG_OBJECT (appsink, "we are flushing");
685
    g_mutex_unlock (appsink->priv->mutex);
686
687
    return GST_FLOW_WRONG_STATE;
  }
David Schleef's avatar
David Schleef committed
688
689
}

690
static GstCaps *
691
gst_app_sink_getcaps (GstBaseSink * psink)
692
693
694
695
{
  GstCaps *caps;

  GstAppSink *appsink = GST_APP_SINK (psink);
David Schleef's avatar
David Schleef committed
696

697
  GST_OBJECT_LOCK (appsink);
698
  if ((caps = appsink->priv->caps))
699
    gst_caps_ref (caps);
700
  GST_DEBUG_OBJECT (appsink, "got caps %" GST_PTR_FORMAT, caps);
701
  GST_OBJECT_UNLOCK (appsink);
David Schleef's avatar
David Schleef committed
702

703
704
  return caps;
}
David Schleef's avatar
David Schleef committed
705
706
707
708
709

/* external API */

/**
 * gst_app_sink_set_caps:
710
711
 * @appsink: a #GstAppSink
 * @caps: caps to set
David Schleef's avatar
David Schleef committed
712
713
 *
 * Set the capabilities on the appsink element.  This function takes
714
 * a copy of the caps structure. After calling this method, the sink will only
715
 * accept caps that match @caps. If @caps is non-fixed, you must check the caps
716
 * on the buffers to get the actual used caps. 
717
718
 *
 * Since: 0.10.22
David Schleef's avatar
David Schleef committed
719
720
 */
void
721
gst_app_sink_set_caps (GstAppSink * appsink, const GstCaps * caps)
David Schleef's avatar
David Schleef committed
722
{
723
724
  GstCaps *old;

David Schleef's avatar
David Schleef committed
725
726
727
  g_return_if_fail (appsink != NULL);
  g_return_if_fail (GST_IS_APP_SINK (appsink));

728
  GST_OBJECT_LOCK (appsink);
729
  GST_DEBUG_OBJECT (appsink, "setting caps to %" GST_PTR_FORMAT, caps);
730
  if ((old = appsink->priv->caps) != caps) {
731
    if (caps)
732
      appsink->priv->caps = gst_caps_copy (caps);
733
    else
734
      appsink->priv->caps = NULL;
735
736
737
    if (old)
      gst_caps_unref (old);
  }
738
  GST_OBJECT_UNLOCK (appsink);
David Schleef's avatar
David Schleef committed
739
740
}

741
/**
742
743
744
745
746
747
 * gst_app_sink_get_caps:
 * @appsink: a #GstAppSink
 *
 * Get the configured caps on @appsink.
 *
 * Returns: the #GstCaps accepted by the sink. gst_caps_unref() after usage.
748
749
 *
 * Since: 0.10.22
750
751
752
753
754
755
756
757
758
759
 */
GstCaps *
gst_app_sink_get_caps (GstAppSink * appsink)
{
  GstCaps *caps;

  g_return_val_if_fail (appsink != NULL, NULL);
  g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);

  GST_OBJECT_LOCK (appsink);
760
  if ((caps = appsink->priv->caps))
761
762
763
764
765
766
767
768
769
    gst_caps_ref (caps);
  GST_DEBUG_OBJECT (appsink, "getting caps of %" GST_PTR_FORMAT, caps);
  GST_OBJECT_UNLOCK (appsink);

  return caps;
}

/**
 * gst_app_sink_is_eos:
770
771
772
773
774
775
776
777
778
 * @appsink: a #GstAppSink
 *
 * Check if @appsink is EOS, which is when no more buffers can be pulled because
 * an EOS event was received.
 *
 * This function also returns %TRUE when the appsink is not in the PAUSED or
 * PLAYING state.
 *
 * Returns: %TRUE if no more buffers can be pulled and the appsink is EOS.
779
780
 *
 * Since: 0.10.22
781
 */
David Schleef's avatar
David Schleef committed
782
gboolean
783
gst_app_sink_is_eos (GstAppSink * appsink)
David Schleef's avatar
David Schleef committed
784
785
786
787
788
789
{
  gboolean ret;

  g_return_val_if_fail (appsink != NULL, FALSE);
  g_return_val_if_fail (GST_IS_APP_SINK (appsink), FALSE);

790
791
  g_mutex_lock (appsink->priv->mutex);
  if (!appsink->priv->started)
792
793
    goto not_started;

794
  if (appsink->priv->is_eos && g_queue_is_empty (appsink->priv->queue)) {
795
    GST_DEBUG_OBJECT (appsink, "we are EOS and the queue is empty");
David Schleef's avatar
David Schleef committed
796
797
    ret = TRUE;
  } else {
798
    GST_DEBUG_OBJECT (appsink, "we are not yet EOS");
David Schleef's avatar
David Schleef committed
799
800
    ret = FALSE;
  }
801
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
802
803

  return ret;
804
805
806
807

not_started:
  {
    GST_DEBUG_OBJECT (appsink, "we are stopped, return TRUE");
808
    g_mutex_unlock (appsink->priv->mutex);
809
810
    return TRUE;
  }
David Schleef's avatar
David Schleef committed
811
812
}

813
814
815
816
817
818
819
820
/**
 * gst_app_sink_set_emit_signals:
 * @appsink: a #GstAppSink
 * @emit: the new state
 *
 * Make appsink emit the "new-preroll" and "new-buffer" signals. This option is
 * by default disabled because signal emission is expensive and unneeded when
 * the application prefers to operate in pull mode.
821
822
 *
 * Since: 0.10.22
823
824
825
826
827
828
 */
void
gst_app_sink_set_emit_signals (GstAppSink * appsink, gboolean emit)
{
  g_return_if_fail (GST_IS_APP_SINK (appsink));

829
830
831
  g_mutex_lock (appsink->priv->mutex);
  appsink->priv->emit_signals = emit;
  g_mutex_unlock (appsink->priv->mutex);
832
833
834
835
836
837
838
839
840
841
}

/**
 * gst_app_sink_get_emit_signals:
 * @appsink: a #GstAppSink
 *
 * Check if appsink will emit the "new-preroll" and "new-buffer" signals.
 *
 * Returns: %TRUE if @appsink is emiting the "new-preroll" and "new-buffer"
 * signals.
842
843
 *
 * Since: 0.10.22
844
845
846
847
848
849
850
851
 */
gboolean
gst_app_sink_get_emit_signals (GstAppSink * appsink)
{
  gboolean result;

  g_return_val_if_fail (GST_IS_APP_SINK (appsink), FALSE);

852
853
854
  g_mutex_lock (appsink->priv->mutex);
  result = appsink->priv->emit_signals;
  g_mutex_unlock (appsink->priv->mutex);
855
856
857
858
859
860
861
862
863
864
865
866

  return result;
}

/**
 * gst_app_sink_set_max_buffers:
 * @appsink: a #GstAppSink
 * @max: the maximum number of buffers to queue
 *
 * Set the maximum amount of buffers that can be queued in @appsink. After this
 * amount of buffers are queued in appsink, any more buffers will block upstream
 * elements until a buffer is pulled from @appsink.
867
868
 *
 * Since: 0.10.22
869
870
871
872
873
874
 */
void
gst_app_sink_set_max_buffers (GstAppSink * appsink, guint max)
{
  g_return_if_fail (GST_IS_APP_SINK (appsink));

875
876
877
  g_mutex_lock (appsink->priv->mutex);
  if (max != appsink->priv->max_buffers) {
    appsink->priv->max_buffers = max;
878
    /* signal the change */
879
    g_cond_signal (appsink->priv->cond);
880
  }
881
  g_mutex_unlock (appsink->priv->mutex);
882
883
884
885
886
887
888
889
890
}

/**
 * gst_app_sink_get_max_buffers:
 * @appsink: a #GstAppSink
 *
 * Get the maximum amount of buffers that can be queued in @appsink.
 *
 * Returns: The maximum amount of buffers that can be queued.
891
892
 *
 * Since: 0.10.22
893
894
895
896
897
898
899
900
 */
guint
gst_app_sink_get_max_buffers (GstAppSink * appsink)
{
  guint result;

  g_return_val_if_fail (GST_IS_APP_SINK (appsink), 0);

901
902
903
  g_mutex_lock (appsink->priv->mutex);
  result = appsink->priv->max_buffers;
  g_mutex_unlock (appsink->priv->mutex);
904
905
906
907

  return result;
}

908
909
910
/**
 * gst_app_sink_set_drop:
 * @appsink: a #GstAppSink
911
 * @drop: the new state
912
913
914
 *
 * Instruct @appsink to drop old buffers when the maximum amount of queued
 * buffers is reached.
915
916
 *
 * Since: 0.10.22
917
918
919
920
921
922
 */
void
gst_app_sink_set_drop (GstAppSink * appsink, gboolean drop)
{
  g_return_if_fail (GST_IS_APP_SINK (appsink));

923
924
925
  g_mutex_lock (appsink->priv->mutex);
  if (appsink->priv->drop != drop) {
    appsink->priv->drop = drop;
926
    /* signal the change */
927
    g_cond_signal (appsink->priv->cond);
928
  }
929
  g_mutex_unlock (appsink->priv->mutex);
930
931
932
933
934
935
936
937
938
939
940
}

/**
 * gst_app_sink_get_drop:
 * @appsink: a #GstAppSink
 *
 * Check if @appsink will drop old buffers when the maximum amount of queued
 * buffers is reached.
 *
 * Returns: %TRUE if @appsink is dropping old buffers when the queue is
 * filled.
941
942
 *
 * Since: 0.10.22
943
944
945
946
947
948
949
950
 */
gboolean
gst_app_sink_get_drop (GstAppSink * appsink)
{
  gboolean result;

  g_return_val_if_fail (GST_IS_APP_SINK (appsink), FALSE);

951
952
953
  g_mutex_lock (appsink->priv->mutex);
  result = appsink->priv->drop;
  g_mutex_unlock (appsink->priv->mutex);
954
955
956
957

  return result;
}

958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
/**
 * gst_app_sink_pull_preroll:
 * @appsink: a #GstAppSink
 *
 * Get the last preroll buffer in @appsink. This was the buffer that caused the
 * appsink to preroll in the PAUSED state. This buffer can be pulled many times
 * and remains available to the application even after EOS.
 *
 * This function is typically used when dealing with a pipeline in the PAUSED
 * state. Calling this function after doing a seek will give the buffer right
 * after the seek position.
 *
 * Note that the preroll buffer will also be returned as the first buffer
 * when calling gst_app_sink_pull_buffer().
 *
973
974
 * If an EOS event was received before any buffers, this function returns
 * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. 
975
976
977
978
979
 *
 * This function blocks until a preroll buffer or EOS is received or the appsink
 * element is set to the READY/NULL state. 
 *
 * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
980
981
 *
 * Since: 0.10.22
982
983
984
985
986
987
988
989
990
 */
GstBuffer *
gst_app_sink_pull_preroll (GstAppSink * appsink)
{
  GstBuffer *buf = NULL;

  g_return_val_if_fail (appsink != NULL, NULL);
  g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);

991
  g_mutex_lock (appsink->priv->mutex);
992
993
994

  while (TRUE) {
    GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
995
    if (!appsink->priv->started)
996
997
      goto not_started;

998
    if (appsink->priv->preroll != NULL)
999
1000
      break;

1001
    if (appsink->priv->is_eos)
1002
1003
1004
1005
      goto eos;

    /* nothing to return, wait */
    GST_DEBUG_OBJECT (appsink, "waiting for the preroll buffer");
1006
    g_cond_wait (appsink->priv->cond, appsink->priv->mutex);
1007
  }
1008
  buf = gst_buffer_ref (appsink->priv->preroll);
1009
  GST_DEBUG_OBJECT (appsink, "we have the preroll buffer %p", buf);
1010
  g_mutex_unlock (appsink->priv->mutex);
1011
1012
1013
1014
1015
1016
1017

  return buf;

  /* special conditions */
eos:
  {
    GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
1018
    g_mutex_unlock (appsink->priv->mutex);
1019
1020
1021
1022
1023
    return NULL;
  }
not_started:
  {
    GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
1024
    g_mutex_unlock (appsink->priv->mutex);
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
    return NULL;
  }
}

/**
 * gst_app_sink_pull_buffer:
 * @appsink: a #GstAppSink
 *
 * This function blocks until a buffer or EOS becomes available or the appsink
 * element is set to the READY/NULL state. 
 *
 * This function will only return buffers when the appsink is in the PLAYING
 * state. All rendered buffers will be put in a queue so that the application
 * can pull buffers at its own rate. Note that when the application does not
 * pull buffers fast enough, the queued buffers could consume a lot of memory,
 * especially when dealing with raw video frames.
 *
 * If an EOS event was received before any buffers, this function returns
1043
 * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. 
1044
1045
 *
 * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
1046
1047
 *
 * Since: 0.10.22
1048
 */
David Schleef's avatar
David Schleef committed
1049
1050
1051
1052
1053
1054
1055
1056
GstBuffer *
gst_app_sink_pull_buffer (GstAppSink * appsink)
{
  GstBuffer *buf = NULL;

  g_return_val_if_fail (appsink != NULL, NULL);
  g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);

1057
  g_mutex_lock (appsink->priv->mutex);
1058
1059
1060

  while (TRUE) {
    GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
1061
    if (!appsink->priv->started)
1062
1063
      goto not_started;

1064
    if (!g_queue_is_empty (appsink->priv->queue))
1065
1066
      break;

1067
    if (appsink->priv->is_eos)
1068
      goto eos;
David Schleef's avatar
David Schleef committed
1069

1070
1071
    /* nothing to return, wait */
    GST_DEBUG_OBJECT (appsink, "waiting for a buffer");
1072
    g_cond_wait (appsink->priv->cond, appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
1073
  }
1074
  buf = g_queue_pop_head (appsink->priv->queue);
1075
  GST_DEBUG_OBJECT (appsink, "we have a buffer %p", buf);
1076
1077
  g_cond_signal (appsink->priv->cond);
  g_mutex_unlock (appsink->priv->mutex);
David Schleef's avatar
David Schleef committed
1078
1079

  return buf;
1080
1081
1082
1083
1084

  /* special conditions */
eos:
  {
    GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
1085
    g_mutex_unlock (appsink->priv->mutex);
1086
1087
1088
1089
1090
    return NULL;
  }
not_started:
  {
    GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
1091
    g_mutex_unlock (appsink->priv->mutex);
1092
1093
    return NULL;
  }
David Schleef's avatar
David Schleef committed
1094
}
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
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

/**
 * gst_app_sink_set_callbacks:
 * @appsink: a #GstAppSink
 * @callbacks: the callbacks
 * @user_data: a user_data argument for the callbacks
 * @notify: a destroy notify function
 *
 * Set callbacks which will be executed for each new preroll, new buffer and eos.
 * This is an alternative to using the signals, it has lower overhead and is thus
 * less expensive, but also less flexible.
 *
 * Since: 0.10.23
 */
void
gst_app_sink_set_callbacks (GstAppSink * appsink,
    GstAppSinkCallbacks * callbacks, gpointer user_data, GDestroyNotify notify)
{
  GDestroyNotify old_notify;

  g_return_if_fail (appsink != NULL);
  g_return_if_fail (GST_IS_APP_SINK (appsink));
  g_return_if_fail (callbacks != NULL);

  GST_OBJECT_LOCK (appsink);
  old_notify = appsink->priv->notify;

  if (old_notify) {
    gpointer old_data;

    old_data = appsink->priv->user_data;

    appsink->priv->user_data = NULL;
    appsink->priv->notify = NULL;
    GST_OBJECT_UNLOCK (appsink);

    old_notify (old_data);

    GST_OBJECT_LOCK (appsink);
  }
  appsink->priv->callbacks = *callbacks;
  appsink->priv->user_data = user_data;
  appsink->priv->notify = notify;
  GST_OBJECT_UNLOCK (appsink);
}