gstappsink.c 52.1 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
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
17 18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
David Schleef's avatar
David Schleef committed
19
 */
20 21
/**
 * SECTION:gstappsink
22
 * @title: GstAppSink
23
 * @short_description: Easy way for applications to extract samples from a
24
 *     pipeline
25
 * @see_also: #GstSample, #GstBaseSink, appsrc
26 27
 *
 * Appsink is a sink plugin that supports many different methods for making
28 29
 * the application get a handle on the GStreamer data in a pipeline. Unlike
 * most GStreamer elements, Appsink provides external API functions.
30
 *
31 32 33
 * 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.
 *
34 35 36
 * The normal way of retrieving samples from appsink is by using the
 * gst_app_sink_pull_sample() and gst_app_sink_pull_preroll() methods.
 * These methods block until a sample becomes available in the sink or when the
37 38 39
 * sink is shut down or reaches EOS. There are also timed variants of these
 * methods, gst_app_sink_try_pull_sample() and gst_app_sink_try_pull_preroll(),
 * which accept a timeout parameter to limit the amount of time to wait.
40 41
 *
 * Appsink will internally use a queue to collect buffers from the streaming
42
 * thread. If the application is not pulling samples fast enough, this queue
43 44 45 46 47 48 49
 * 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
50 51
 * to %TRUE will make appsink emit the "new-sample" and "new-preroll" signals
 * when a sample can be pulled without blocking.
52 53 54
 *
 * 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
55
 * the pulled samples can be obtained by getting the sample caps.
56
 *
57
 * If one of the pull-preroll or pull-sample methods return %NULL, the appsink
58 59 60 61 62
 * 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.
63 64
 */

David Schleef's avatar
David Schleef committed
65 66 67 68 69
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
70
#include <gst/base/base.h>
David Schleef's avatar
David Schleef committed
71 72 73 74 75

#include <string.h>

#include "gstappsink.h"

76 77 78 79 80 81 82
typedef enum
{
  NOONE_WAITING,
  STREAM_WAITING,               /* streaming thread is waiting for application thread */
  APP_WAITING,                  /* application thread is waiting for streaming thread */
} GstAppSinkWaitStatus;

83 84 85 86
struct _GstAppSinkPrivate
{
  GstCaps *caps;
  gboolean emit_signals;
87
  guint num_buffers;
88 89
  guint max_buffers;
  gboolean drop;
90
  gboolean wait_on_eos;
91
  GstAppSinkWaitStatus wait_status;
92

93 94
  GCond cond;
  GMutex mutex;
95
  GstQueueArray *queue;
96
  GstBuffer *preroll_buffer;
97 98
  GstCaps *preroll_caps;
  GstCaps *last_caps;
99
  GstSegment preroll_segment;
100
  GstSegment last_segment;
101
  gboolean flushing;
Wim Taymans's avatar
Wim Taymans committed
102
  gboolean unlock;
103 104
  gboolean started;
  gboolean is_eos;
105
  gboolean buffer_lists_supported;
106 107 108 109

  GstAppSinkCallbacks callbacks;
  gpointer user_data;
  GDestroyNotify notify;
110 111

  GstSample *sample;
112
};
David Schleef's avatar
David Schleef committed
113

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

enum
{
119 120 121
  /* signals */
  SIGNAL_EOS,
  SIGNAL_NEW_PREROLL,
122
  SIGNAL_NEW_SAMPLE,
123

124
  /* actions */
125
  SIGNAL_PULL_PREROLL,
126
  SIGNAL_PULL_SAMPLE,
127 128
  SIGNAL_TRY_PULL_PREROLL,
  SIGNAL_TRY_PULL_SAMPLE,
129 130 131 132

  LAST_SIGNAL
};

133
#define DEFAULT_PROP_EOS		TRUE
134
#define DEFAULT_PROP_EMIT_SIGNALS	FALSE
135
#define DEFAULT_PROP_MAX_BUFFERS	0
136
#define DEFAULT_PROP_DROP		FALSE
137
#define DEFAULT_PROP_WAIT_ON_EOS	TRUE
138
#define DEFAULT_PROP_BUFFER_LIST	FALSE
139

140 141 142 143
enum
{
  PROP_0,
  PROP_CAPS,
144 145 146
  PROP_EOS,
  PROP_EMIT_SIGNALS,
  PROP_MAX_BUFFERS,
147
  PROP_DROP,
148
  PROP_WAIT_ON_EOS,
149
  PROP_BUFFER_LIST,
150
  PROP_LAST
David Schleef's avatar
David Schleef committed
151 152 153 154 155 156 157 158
};

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

159 160 161
static void gst_app_sink_uri_handler_init (gpointer g_iface,
    gpointer iface_data);

162 163 164
static void gst_app_sink_dispose (GObject * object);
static void gst_app_sink_finalize (GObject * object);

David Schleef's avatar
David Schleef committed
165 166 167 168
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);
169

170 171
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
172 173 174
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);
175
static gboolean gst_app_sink_query (GstBaseSink * bsink, GstQuery * query);
176 177
static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
    GstBuffer * buffer);
178 179
static GstFlowReturn gst_app_sink_render_common (GstBaseSink * psink,
    GstMiniObject * data, gboolean is_list);
David Schleef's avatar
David Schleef committed
180 181
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
    GstBuffer * buffer);
182 183
static GstFlowReturn gst_app_sink_render_list (GstBaseSink * psink,
    GstBufferList * list);
184
static gboolean gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps);
185
static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter);
186 187

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

189 190 191 192
#define gst_app_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstAppSink, gst_app_sink, GST_TYPE_BASE_SINK,
    G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
        gst_app_sink_uri_handler_init));
David Schleef's avatar
David Schleef committed
193 194 195 196 197

static void
gst_app_sink_class_init (GstAppSinkClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
198
  GstElementClass *element_class = (GstElementClass *) klass;
David Schleef's avatar
David Schleef committed
199 200
  GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;

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

203 204 205
  gobject_class->dispose = gst_app_sink_dispose;
  gobject_class->finalize = gst_app_sink_finalize;

David Schleef's avatar
David Schleef committed
206 207
  gobject_class->set_property = gst_app_sink_set_property;
  gobject_class->get_property = gst_app_sink_get_property;
208 209 210

  g_object_class_install_property (gobject_class, PROP_CAPS,
      g_param_spec_boxed ("caps", "Caps",
211
          "The allowed caps for the sink pad", GST_TYPE_CAPS,
212
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
213 214 215

  g_object_class_install_property (gobject_class, PROP_EOS,
      g_param_spec_boolean ("eos", "EOS",
216 217 218 219 220
          "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",
221
          "Emit new-preroll and new-sample signals",
222
          DEFAULT_PROP_EMIT_SIGNALS,
223 224 225 226
          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",
227
          "The maximum number of buffers to queue internally (0 = unlimited)",
228 229
          0, G_MAXUINT, DEFAULT_PROP_MAX_BUFFERS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230

231 232 233 234 235
  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));

236 237 238 239
  g_object_class_install_property (gobject_class, PROP_BUFFER_LIST,
      g_param_spec_boolean ("buffer-list", "Buffer List",
          "Use buffer lists", DEFAULT_PROP_BUFFER_LIST,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
  /**
   * GstAppSink::wait-on-eos:
   *
   * Wait for all buffers to be processed after receiving an EOS.
   *
   * In cases where it is uncertain if an @appsink will have a consumer for its buffers
   * when it receives an EOS, set to %FALSE to ensure that the @appsink will not hang.
   *
   * Since: 1.8
   */
  g_object_class_install_property (gobject_class, PROP_WAIT_ON_EOS,
      g_param_spec_boolean ("wait-on-eos", "Wait on EOS",
          "Wait for all buffers to be processed after receiving an EOS",
          DEFAULT_PROP_WAIT_ON_EOS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

256 257
  /**
   * GstAppSink::eos:
Piotr Fusik's avatar
Piotr Fusik committed
258
   * @appsink: the appsink element that emitted the signal
259
   *
Piotr Fusik's avatar
Piotr Fusik committed
260
   * Signal that the end-of-stream has been reached. This signal is emitted from
261
   * the streaming thread.
262 263 264 265 266 267 268
   */
  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:
Piotr Fusik's avatar
Piotr Fusik committed
269
   * @appsink: the appsink element that emitted the signal
270
   *
271
   * Signal that a new preroll sample is available.
272
   *
273
   * This signal is emitted from the streaming thread and only when the
Stefan Kost's avatar
Stefan Kost committed
274
   * "emit-signals" property is %TRUE.
275
   *
276
   * The new preroll sample can be retrieved with the "pull-preroll" action
277 278 279
   * signal or gst_app_sink_pull_preroll() either from this signal callback
   * or from any other thread.
   *
Piotr Fusik's avatar
Piotr Fusik committed
280
   * Note that this signal is only emitted when the "emit-signals" property is
281
   * set to %TRUE, which it is not by default for performance reasons.
282 283 284 285
   */
  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),
286
      NULL, NULL, NULL, GST_TYPE_FLOW_RETURN, 0, G_TYPE_NONE);
287
  /**
288
   * GstAppSink::new-sample:
Wim Taymans's avatar
Wim Taymans committed
289 290
   * @appsink: the appsink element that emited the signal
   *
291
   * Signal that a new sample is available.
Wim Taymans's avatar
Wim Taymans committed
292
   *
293
   * This signal is emitted from the streaming thread and only when the
Stefan Kost's avatar
Stefan Kost committed
294
   * "emit-signals" property is %TRUE.
Wim Taymans's avatar
Wim Taymans committed
295
   *
296 297
   * The new sample can be retrieved with the "pull-sample" action
   * signal or gst_app_sink_pull_sample() either from this signal callback
Wim Taymans's avatar
Wim Taymans committed
298 299
   * or from any other thread.
   *
Piotr Fusik's avatar
Piotr Fusik committed
300
   * Note that this signal is only emitted when the "emit-signals" property is
Wim Taymans's avatar
Wim Taymans committed
301 302
   * set to %TRUE, which it is not by default for performance reasons.
   */
303 304 305
  gst_app_sink_signals[SIGNAL_NEW_SAMPLE] =
      g_signal_new ("new-sample", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstAppSinkClass, new_sample),
306
      NULL, NULL, NULL, GST_TYPE_FLOW_RETURN, 0, G_TYPE_NONE);
307 308 309 310 311

  /**
   * GstAppSink::pull-preroll:
   * @appsink: the appsink element to emit this signal on
   *
312
   * Get the last preroll sample in @appsink. This was the sample that caused the
313
   * appsink to preroll in the PAUSED state.
314 315
   *
   * This function is typically used when dealing with a pipeline in the PAUSED
316
   * state. Calling this function after doing a seek will give the sample right
317 318
   * after the seek position.
   *
319 320 321
   * Calling this function will clear the internal reference to the preroll
   * buffer.
   *
322 323
   * Note that the preroll sample will also be returned as the first sample
   * when calling gst_app_sink_pull_sample() or the "pull-sample" action signal.
324 325
   *
   * If an EOS event was received before any buffers, this function returns
Stefan Kost's avatar
Stefan Kost committed
326
   * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
327
   *
328
   * This function blocks until a preroll sample or EOS is received or the appsink
Stefan Kost's avatar
Stefan Kost committed
329
   * element is set to the READY/NULL state.
330
   *
331
   * Returns: a #GstSample or NULL when the appsink is stopped or EOS.
332 333 334
   */
  gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
      g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass),
335
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
336
          pull_preroll), NULL, NULL, NULL, GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
Wim Taymans's avatar
Wim Taymans committed
337
  /**
338
   * GstAppSink::pull-sample:
Wim Taymans's avatar
Wim Taymans committed
339 340
   * @appsink: the appsink element to emit this signal on
   *
341
   * This function blocks until a sample or EOS becomes available or the appsink
Stefan Kost's avatar
Stefan Kost committed
342
   * element is set to the READY/NULL state.
Wim Taymans's avatar
Wim Taymans committed
343
   *
344 345 346
   * This function will only return samples when the appsink is in the PLAYING
   * state. All rendered samples will be put in a queue so that the application
   * can pull samples at its own rate.
Wim Taymans's avatar
Wim Taymans committed
347
   *
348 349
   * Note that when the application does not pull samples fast enough, the
   * queued samples could consume a lot of memory, especially when dealing with
Wim Taymans's avatar
Wim Taymans committed
350 351 352 353
   * raw video frames. It's possible to control the behaviour of the queue with
   * the "drop" and "max-buffers" properties.
   *
   * If an EOS event was received before any buffers, this function returns
Stefan Kost's avatar
Stefan Kost committed
354
   * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
Wim Taymans's avatar
Wim Taymans committed
355
   *
356
   * Returns: a #GstSample or NULL when the appsink is stopped or EOS.
Wim Taymans's avatar
Wim Taymans committed
357
   */
358 359
  gst_app_sink_signals[SIGNAL_PULL_SAMPLE] =
      g_signal_new ("pull-sample", G_TYPE_FROM_CLASS (klass),
Wim Taymans's avatar
Wim Taymans committed
360
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
361
          pull_sample), NULL, NULL, NULL, GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
362 363 364 365 366 367
  /**
   * GstAppSink::try-pull-preroll:
   * @appsink: the appsink element to emit this signal on
   * @timeout: the maximum amount of time to wait for the preroll sample
   *
   * Get the last preroll sample in @appsink. This was the sample that caused the
368
   * appsink to preroll in the PAUSED state.
369 370 371 372 373
   *
   * This function is typically used when dealing with a pipeline in the PAUSED
   * state. Calling this function after doing a seek will give the sample right
   * after the seek position.
   *
374 375 376
   * Calling this function will clear the internal reference to the preroll
   * buffer.
   *
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
   * Note that the preroll sample will also be returned as the first sample
   * when calling gst_app_sink_pull_sample() or the "pull-sample" action signal.
   *
   * If an EOS event was received before any buffers or the timeout expires,
   * this function returns %NULL. Use gst_app_sink_is_eos () to check for the EOS
   * condition.
   *
   * This function blocks until a preroll sample or EOS is received, the appsink
   * element is set to the READY/NULL state, or the timeout expires.
   *
   * Returns: a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
   *
   * Since: 1.10
   */
  gst_app_sink_signals[SIGNAL_TRY_PULL_PREROLL] =
      g_signal_new ("try-pull-preroll", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstAppSinkClass, try_pull_preroll), NULL, NULL, NULL,
      GST_TYPE_SAMPLE, 1, GST_TYPE_CLOCK_TIME);
  /**
   * GstAppSink::try-pull-sample:
   * @appsink: the appsink element to emit this signal on
   * @timeout: the maximum amount of time to wait for a sample
   *
   * This function blocks until a sample or EOS becomes available or the appsink
   * element is set to the READY/NULL state or the timeout expires.
   *
   * This function will only return samples when the appsink is in the PLAYING
   * state. All rendered samples will be put in a queue so that the application
   * can pull samples at its own rate.
   *
   * Note that when the application does not pull samples fast enough, the
   * queued samples 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.
   *
   * If an EOS event was received before any buffers or the timeout expires,
   * this function returns %NULL. Use gst_app_sink_is_eos () to check
   * for the EOS condition.
   *
   * Returns: a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
   *
   * Since: 1.10
   */
  gst_app_sink_signals[SIGNAL_TRY_PULL_SAMPLE] =
      g_signal_new ("try-pull-sample", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstAppSinkClass, try_pull_sample), NULL, NULL, NULL,
      GST_TYPE_SAMPLE, 1, GST_TYPE_CLOCK_TIME);
David Schleef's avatar
David Schleef committed
426

427
  gst_element_class_set_static_metadata (element_class, "AppSink",
428 429 430
      "Generic/Sink", "Allow the application to get access to raw buffer",
      "David Schleef <ds@schleef.org>, Wim Taymans <wim.taymans@gmail.com>");

431 432
  gst_element_class_add_static_pad_template (element_class,
      &gst_app_sink_template);
433

434 435
  basesink_class->unlock = gst_app_sink_unlock_start;
  basesink_class->unlock_stop = gst_app_sink_unlock_stop;
David Schleef's avatar
David Schleef committed
436 437 438
  basesink_class->start = gst_app_sink_start;
  basesink_class->stop = gst_app_sink_stop;
  basesink_class->event = gst_app_sink_event;
439
  basesink_class->preroll = gst_app_sink_preroll;
David Schleef's avatar
David Schleef committed
440
  basesink_class->render = gst_app_sink_render;
441
  basesink_class->render_list = gst_app_sink_render_list;
442
  basesink_class->get_caps = gst_app_sink_getcaps;
443
  basesink_class->set_caps = gst_app_sink_setcaps;
444
  basesink_class->query = gst_app_sink_query;
445 446

  klass->pull_preroll = gst_app_sink_pull_preroll;
447
  klass->pull_sample = gst_app_sink_pull_sample;
448 449
  klass->try_pull_preroll = gst_app_sink_try_pull_preroll;
  klass->try_pull_sample = gst_app_sink_try_pull_sample;
450 451

  g_type_class_add_private (klass, sizeof (GstAppSinkPrivate));
452 453 454
}

static void
455
gst_app_sink_init (GstAppSink * appsink)
456
{
Wim Taymans's avatar
Wim Taymans committed
457 458 459 460
  GstAppSinkPrivate *priv;

  priv = appsink->priv =
      G_TYPE_INSTANCE_GET_PRIVATE (appsink, GST_TYPE_APP_SINK,
461
      GstAppSinkPrivate);
462

463 464
  g_mutex_init (&priv->mutex);
  g_cond_init (&priv->cond);
465
  priv->queue = gst_queue_array_new (16);
466
  priv->sample = gst_sample_new (NULL, NULL, NULL, NULL);
467

Wim Taymans's avatar
Wim Taymans committed
468 469 470
  priv->emit_signals = DEFAULT_PROP_EMIT_SIGNALS;
  priv->max_buffers = DEFAULT_PROP_MAX_BUFFERS;
  priv->drop = DEFAULT_PROP_DROP;
471
  priv->wait_on_eos = DEFAULT_PROP_WAIT_ON_EOS;
472
  priv->buffer_lists_supported = DEFAULT_PROP_BUFFER_LIST;
473
  priv->wait_status = NOONE_WAITING;
David Schleef's avatar
David Schleef committed
474 475 476 477 478
}

static void
gst_app_sink_dispose (GObject * obj)
{
Wim Taymans's avatar
Wim Taymans committed
479 480
  GstAppSink *appsink = GST_APP_SINK_CAST (obj);
  GstAppSinkPrivate *priv = appsink->priv;
Branko Subasic's avatar
Branko Subasic committed
481
  GstMiniObject *queue_obj;
David Schleef's avatar
David Schleef committed
482

483
  GST_OBJECT_LOCK (appsink);
Wim Taymans's avatar
Wim Taymans committed
484 485 486
  if (priv->caps) {
    gst_caps_unref (priv->caps);
    priv->caps = NULL;
David Schleef's avatar
David Schleef committed
487
  }
Wim Taymans's avatar
Wim Taymans committed
488 489
  if (priv->notify) {
    priv->notify (priv->user_data);
490
  }
Wim Taymans's avatar
Wim Taymans committed
491 492
  priv->user_data = NULL;
  priv->notify = NULL;
493

494 495
  GST_OBJECT_UNLOCK (appsink);

496
  g_mutex_lock (&priv->mutex);
497
  while ((queue_obj = gst_queue_array_pop_head (priv->queue)))
Branko Subasic's avatar
Branko Subasic committed
498
    gst_mini_object_unref (queue_obj);
499
  gst_buffer_replace (&priv->preroll_buffer, NULL);
500 501
  gst_caps_replace (&priv->preroll_caps, NULL);
  gst_caps_replace (&priv->last_caps, NULL);
502 503 504 505
  if (priv->sample) {
    gst_sample_unref (priv->sample);
    priv->sample = NULL;
  }
506
  g_mutex_unlock (&priv->mutex);
David Schleef's avatar
David Schleef committed
507 508 509 510 511

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

static void
512
gst_app_sink_finalize (GObject * obj)
David Schleef's avatar
David Schleef committed
513
{
Wim Taymans's avatar
Wim Taymans committed
514 515
  GstAppSink *appsink = GST_APP_SINK_CAST (obj);
  GstAppSinkPrivate *priv = appsink->priv;
516

517 518
  g_mutex_clear (&priv->mutex);
  g_cond_clear (&priv->cond);
519
  gst_queue_array_free (priv->queue);
520 521

  G_OBJECT_CLASS (parent_class)->finalize (obj);
David Schleef's avatar
David Schleef committed
522 523 524 525 526 527
}

static void
gst_app_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
Wim Taymans's avatar
Wim Taymans committed
528
  GstAppSink *appsink = GST_APP_SINK_CAST (object);
David Schleef's avatar
David Schleef committed
529 530

  switch (prop_id) {
531 532 533
    case PROP_CAPS:
      gst_app_sink_set_caps (appsink, gst_value_get_caps (value));
      break;
534 535 536 537 538 539
    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;
540 541 542
    case PROP_DROP:
      gst_app_sink_set_drop (appsink, g_value_get_boolean (value));
      break;
543 544 545 546
    case PROP_BUFFER_LIST:
      gst_app_sink_set_buffer_list_support (appsink,
          g_value_get_boolean (value));
      break;
547 548 549
    case PROP_WAIT_ON_EOS:
      gst_app_sink_set_wait_on_eos (appsink, g_value_get_boolean (value));
      break;
David Schleef's avatar
David Schleef committed
550 551 552 553 554 555 556 557 558 559
    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)
{
Wim Taymans's avatar
Wim Taymans committed
560
  GstAppSink *appsink = GST_APP_SINK_CAST (object);
David Schleef's avatar
David Schleef committed
561 562

  switch (prop_id) {
563 564 565 566 567 568 569 570 571 572 573 574 575
    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;
576 577 578 579 580 581
    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;
582 583 584
    case PROP_DROP:
      g_value_set_boolean (value, gst_app_sink_get_drop (appsink));
      break;
585 586 587 588
    case PROP_BUFFER_LIST:
      g_value_set_boolean (value,
          gst_app_sink_get_buffer_list_support (appsink));
      break;
589 590 591
    case PROP_WAIT_ON_EOS:
      g_value_set_boolean (value, gst_app_sink_get_wait_on_eos (appsink));
      break;
David Schleef's avatar
David Schleef committed
592 593 594 595 596 597
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

598 599 600
static gboolean
gst_app_sink_unlock_start (GstBaseSink * bsink)
{
Wim Taymans's avatar
Wim Taymans committed
601 602
  GstAppSink *appsink = GST_APP_SINK_CAST (bsink);
  GstAppSinkPrivate *priv = appsink->priv;
603

604
  g_mutex_lock (&priv->mutex);
605
  GST_DEBUG_OBJECT (appsink, "unlock start");
Wim Taymans's avatar
Wim Taymans committed
606
  priv->unlock = TRUE;
607 608
  g_cond_signal (&priv->cond);
  g_mutex_unlock (&priv->mutex);
609 610 611 612 613 614 615

  return TRUE;
}

static gboolean
gst_app_sink_unlock_stop (GstBaseSink * bsink)
{
Wim Taymans's avatar
Wim Taymans committed
616 617
  GstAppSink *appsink = GST_APP_SINK_CAST (bsink);
  GstAppSinkPrivate *priv = appsink->priv;
618

619
  g_mutex_lock (&priv->mutex);
620
  GST_DEBUG_OBJECT (appsink, "unlock stop");
Wim Taymans's avatar
Wim Taymans committed
621
  priv->unlock = FALSE;
622 623
  g_cond_signal (&priv->cond);
  g_mutex_unlock (&priv->mutex);
624 625 626 627

  return TRUE;
}

628 629 630
static void
gst_app_sink_flush_unlocked (GstAppSink * appsink)
{
Branko Subasic's avatar
Branko Subasic committed
631
  GstMiniObject *obj;
Wim Taymans's avatar
Wim Taymans committed
632
  GstAppSinkPrivate *priv = appsink->priv;
633

634
  GST_DEBUG_OBJECT (appsink, "flush stop appsink");
Wim Taymans's avatar
Wim Taymans committed
635
  priv->is_eos = FALSE;
636
  gst_buffer_replace (&priv->preroll_buffer, NULL);
637
  while ((obj = gst_queue_array_pop_head (priv->queue)))
Branko Subasic's avatar
Branko Subasic committed
638
    gst_mini_object_unref (obj);
639
  priv->num_buffers = 0;
640
  g_cond_signal (&priv->cond);
641 642
}

David Schleef's avatar
David Schleef committed
643 644 645
static gboolean
gst_app_sink_start (GstBaseSink * psink)
{
Wim Taymans's avatar
Wim Taymans committed
646 647
  GstAppSink *appsink = GST_APP_SINK_CAST (psink);
  GstAppSinkPrivate *priv = appsink->priv;
David Schleef's avatar
David Schleef committed
648

649
  g_mutex_lock (&priv->mutex);
650
  GST_DEBUG_OBJECT (appsink, "starting");
651
  priv->wait_status = NOONE_WAITING;
652
  priv->flushing = FALSE;
Wim Taymans's avatar
Wim Taymans committed
653
  priv->started = TRUE;
654
  gst_segment_init (&priv->preroll_segment, GST_FORMAT_TIME);
655
  gst_segment_init (&priv->last_segment, GST_FORMAT_TIME);
656 657 658 659 660
  priv->sample = gst_sample_make_writable (priv->sample);
  gst_sample_set_buffer (priv->sample, NULL);
  gst_sample_set_buffer_list (priv->sample, NULL);
  gst_sample_set_caps (priv->sample, NULL);
  gst_sample_set_segment (priv->sample, NULL);
661
  g_mutex_unlock (&priv->mutex);
David Schleef's avatar
David Schleef committed
662 663 664 665 666 667 668

  return TRUE;
}

static gboolean
gst_app_sink_stop (GstBaseSink * psink)
{
Wim Taymans's avatar
Wim Taymans committed
669 670
  GstAppSink *appsink = GST_APP_SINK_CAST (psink);
  GstAppSinkPrivate *priv = appsink->priv;
671

672
  g_mutex_lock (&priv->mutex);
673
  GST_DEBUG_OBJECT (appsink, "stopping");
Wim Taymans's avatar
Wim Taymans committed
674 675
  priv->flushing = TRUE;
  priv->started = FALSE;
676
  priv->wait_status = NOONE_WAITING;
677
  gst_app_sink_flush_unlocked (appsink);
678
  gst_buffer_replace (&priv->preroll_buffer, NULL);
679 680
  gst_caps_replace (&priv->preroll_caps, NULL);
  gst_caps_replace (&priv->last_caps, NULL);
681 682
  gst_segment_init (&priv->preroll_segment, GST_FORMAT_UNDEFINED);
  gst_segment_init (&priv->last_segment, GST_FORMAT_UNDEFINED);
683
  g_mutex_unlock (&priv->mutex);
684 685 686 687 688 689 690 691 692 693

  return TRUE;
}

static gboolean
gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps)
{
  GstAppSink *appsink = GST_APP_SINK_CAST (sink);
  GstAppSinkPrivate *priv = appsink->priv;

694
  g_mutex_lock (&priv->mutex);
695
  GST_DEBUG_OBJECT (appsink, "receiving CAPS");
696
  gst_queue_array_push_tail (priv->queue, gst_event_new_caps (caps));
697
  if (!priv->preroll_buffer)
698
    gst_caps_replace (&priv->preroll_caps, caps);
699
  g_mutex_unlock (&priv->mutex);
David Schleef's avatar
David Schleef committed
700 701 702 703 704 705 706

  return TRUE;
}

static gboolean
gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
{
Wim Taymans's avatar
Wim Taymans committed
707 708
  GstAppSink *appsink = GST_APP_SINK_CAST (sink);
  GstAppSinkPrivate *priv = appsink->priv;
David Schleef's avatar
David Schleef committed
709 710

  switch (event->type) {
711
    case GST_EVENT_SEGMENT:
712
      g_mutex_lock (&priv->mutex);
713
      GST_DEBUG_OBJECT (appsink, "receiving SEGMENT");
714
      gst_queue_array_push_tail (priv->queue, gst_event_ref (event));
715
      if (!priv->preroll_buffer)
716
        gst_event_copy_segment (event, &priv->preroll_segment);
717
      g_mutex_unlock (&priv->mutex);
718
      break;
719 720 721
    case GST_EVENT_EOS:{
      gboolean emit = TRUE;

722
      g_mutex_lock (&priv->mutex);
723
      GST_DEBUG_OBJECT (appsink, "receiving EOS");
Wim Taymans's avatar
Wim Taymans committed
724
      priv->is_eos = TRUE;
725 726
      g_cond_signal (&priv->cond);
      g_mutex_unlock (&priv->mutex);
727

728 729 730 731 732
      g_mutex_lock (&priv->mutex);
      /* wait until all buffers are consumed or we're flushing.
       * Otherwise we might signal EOS before all buffers are
       * consumed, which is a bit confusing for the application
       */
733 734
      while (priv->num_buffers > 0 && !priv->flushing && priv->wait_on_eos) {
        priv->wait_status = STREAM_WAITING;
735
        g_cond_wait (&priv->cond, &priv->mutex);
736 737
        priv->wait_status = NOONE_WAITING;
      }
738 739 740 741 742 743 744 745 746 747 748
      if (priv->flushing)
        emit = FALSE;
      g_mutex_unlock (&priv->mutex);

      if (emit) {
        /* emit EOS now */
        if (priv->callbacks.eos)
          priv->callbacks.eos (appsink, priv->user_data);
        else
          g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_EOS], 0);
      }
749

750
      break;
751
    }
752
    case GST_EVENT_FLUSH_START:
753 754 755
      /* 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");
756 757
      break;
    case GST_EVENT_FLUSH_STOP:
758
      g_mutex_lock (&priv->mutex);
759 760
      GST_DEBUG_OBJECT (appsink, "received FLUSH_STOP");
      gst_app_sink_flush_unlocked (appsink);
761
      g_mutex_unlock (&priv->mutex);
David Schleef's avatar
David Schleef committed
762 763 764 765
      break;
    default:
      break;
  }
766
  return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
767
}
David Schleef's avatar
David Schleef committed
768

769 770 771
static GstFlowReturn
gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer)
{
772
  GstFlowReturn res;
Wim Taymans's avatar
Wim Taymans committed
773 774
  GstAppSink *appsink = GST_APP_SINK_CAST (psink);
  GstAppSinkPrivate *priv = appsink->priv;
775
  gboolean emit;
776

777
  g_mutex_lock (&priv->mutex);
Wim Taymans's avatar
Wim Taymans committed
778
  if (priv->flushing)
779 780
    goto flushing;

781
  GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer);
782
  gst_buffer_replace (&priv->preroll_buffer, buffer);
783

784 785 786
  if (priv->wait_status == APP_WAITING)
    g_cond_signal (&priv->cond);

Wim Taymans's avatar
Wim Taymans committed
787
  emit = priv->emit_signals;
788
  g_mutex_unlock (&priv->mutex);
789

790
  if (priv->callbacks.new_preroll) {
Wim Taymans's avatar
Wim Taymans committed
791
    res = priv->callbacks.new_preroll (appsink, priv->user_data);
792 793 794 795 796 797
  } else {
    res = GST_FLOW_OK;
    if (emit)
      g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_PREROLL], 0,
          &res);
  }
798

799
  return res;
800 801 802 803

flushing:
  {
    GST_DEBUG_OBJECT (appsink, "we are flushing");
804
    g_mutex_unlock (&priv->mutex);
805
    return GST_FLOW_FLUSHING;
806
  }
David Schleef's avatar
David Schleef committed
807 808
}

809
static GstMiniObject *
810 811 812
dequeue_buffer (GstAppSink * appsink)
{
  GstAppSinkPrivate *priv = appsink->priv;
813
  GstMiniObject *obj;
814 815

  do {
816
    obj = gst_queue_array_pop_head (priv->queue);
817

818 819
    if (GST_IS_BUFFER (obj) || GST_IS_BUFFER_LIST (obj)) {
      GST_DEBUG_OBJECT (appsink, "dequeued buffer/list %p", obj);
820 821 822 823 824 825 826 827 828 829 830 831
      priv->num_buffers--;
      break;
    } else if (GST_IS_EVENT (obj)) {
      GstEvent *event = GST_EVENT_CAST (obj);

      switch (GST_EVENT_TYPE (obj)) {
        case GST_EVENT_CAPS:
        {
          GstCaps *caps;

          gst_event_parse_caps (event, &caps);
          GST_DEBUG_OBJECT (appsink, "activating caps %" GST_PTR_FORMAT, caps);
Matej Knopp's avatar
Matej Knopp committed
832
          gst_caps_replace (&priv->last_caps, caps);
833 834
          priv->sample = gst_sample_make_writable (priv->sample);
          gst_sample_set_caps (priv->sample, priv->last_caps);
835 836 837 838
          break;
        }
        case GST_EVENT_SEGMENT:
          gst_event_copy_segment (event, &priv->last_segment);
839 840
          priv->sample = gst_sample_make_writable (priv->sample);
          gst_sample_set_segment (priv->sample, &priv->last_segment);
841 842 843 844 845 846 847 848 849 850
          GST_DEBUG_OBJECT (appsink, "activated segment %" GST_SEGMENT_FORMAT,
              &priv->last_segment);
          break;
        default:
          break;
      }
      gst_mini_object_unref (obj);