gstbasesink.c 30.9 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* GStreamer
 * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
 *
 * gstbasesink.c: 
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "gstbasesink.h"
#include <gst/gstmarshal.h>

29 30
GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
#define GST_CAT_DEFAULT gst_base_sink_debug
Wim Taymans's avatar
Wim Taymans committed
31 32 33 34 35 36 37 38 39

/* BaseSink signals and properties */
enum
{
  /* FILL ME */
  SIGNAL_HANDOFF,
  LAST_SIGNAL
};

Wim Taymans's avatar
Wim Taymans committed
40
/* FIXME, need to figure out a better way to handle the pull mode */
Wim Taymans's avatar
Wim Taymans committed
41 42 43 44 45 46 47 48 49 50 51 52
#define DEFAULT_SIZE 1024
#define DEFAULT_HAS_LOOP FALSE
#define DEFAULT_HAS_CHAIN TRUE

enum
{
  PROP_0,
  PROP_HAS_LOOP,
  PROP_HAS_CHAIN,
  PROP_PREROLL_QUEUE_LEN
};

Wim Taymans's avatar
Wim Taymans committed
53 54
static GstElementClass *parent_class = NULL;

55 56 57 58
static void gst_base_sink_base_init (gpointer g_class);
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
59 60

GType
61
gst_base_sink_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
62
{
63
  static GType base_sink_type = 0;
Wim Taymans's avatar
Wim Taymans committed
64

65 66
  if (!base_sink_type) {
    static const GTypeInfo base_sink_info = {
Wim Taymans's avatar
Wim Taymans committed
67
      sizeof (GstBaseSinkClass),
68
      (GBaseInitFunc) gst_base_sink_base_init,
Wim Taymans's avatar
Wim Taymans committed
69
      NULL,
70
      (GClassInitFunc) gst_base_sink_class_init,
Wim Taymans's avatar
Wim Taymans committed
71 72 73 74
      NULL,
      NULL,
      sizeof (GstBaseSink),
      0,
75
      (GInstanceInitFunc) gst_base_sink_init,
Wim Taymans's avatar
Wim Taymans committed
76
    };
Wim Taymans's avatar
Wim Taymans committed
77

78 79
    base_sink_type = g_type_register_static (GST_TYPE_ELEMENT,
        "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
Wim Taymans's avatar
Wim Taymans committed
80
  }
81
  return base_sink_type;
Wim Taymans's avatar
Wim Taymans committed
82
}
Wim Taymans's avatar
Wim Taymans committed
83

84
static void gst_base_sink_set_clock (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
85

86
static void gst_base_sink_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
87
    const GValue * value, GParamSpec * pspec);
88
static void gst_base_sink_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
89 90 91 92
    GValue * value, GParamSpec * pspec);

static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
93 94
static GstFlowReturn gst_base_sink_buffer_alloc (GstBaseSink * sink,
    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
95
static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
96 97
    GstClockTime * start, GstClockTime * end);

98
static GstElementStateReturn gst_base_sink_change_state (GstElement * element);
Wim Taymans's avatar
Wim Taymans committed
99

100 101 102 103 104 105 106
static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
static void gst_base_sink_loop (GstPad * pad);
static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_base_sink_activate_push (GstPad * pad, gboolean active);
static gboolean gst_base_sink_activate_pull (GstPad * pad, gboolean active);
static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
static inline GstFlowReturn gst_base_sink_handle_buffer (GstBaseSink * basesink,
Wim Taymans's avatar
Wim Taymans committed
107
    GstBuffer * buf);
108
static inline gboolean gst_base_sink_handle_event (GstBaseSink * basesink,
109
    GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
110 111

static void
112
gst_base_sink_base_init (gpointer g_class)
Wim Taymans's avatar
Wim Taymans committed
113
{
114
  GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
Wim Taymans's avatar
Wim Taymans committed
115
      "basesink element");
Wim Taymans's avatar
Wim Taymans committed
116 117 118
}

static void
119
gst_base_sink_class_init (GstBaseSinkClass * klass)
Wim Taymans's avatar
Wim Taymans committed
120 121 122 123 124 125 126
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

Wim Taymans's avatar
Wim Taymans committed
127 128
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

129 130 131
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_sink_finalize);
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_sink_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_sink_get_property);
Wim Taymans's avatar
Wim Taymans committed
132 133 134 135 136 137 138 139 140

  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_LOOP,
      g_param_spec_boolean ("has-loop", "has-loop",
          "Enable loop-based operation", DEFAULT_HAS_LOOP,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN,
      g_param_spec_boolean ("has-chain", "has-chain",
          "Enable chain-based operation", DEFAULT_HAS_CHAIN,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Wim Taymans's avatar
Wim Taymans committed
141 142
  /* FIXME, this next value should be configured using an event from the
   * upstream element */
Wim Taymans's avatar
Wim Taymans committed
143 144 145 146 147 148
  g_object_class_install_property (G_OBJECT_CLASS (klass),
      PROP_PREROLL_QUEUE_LEN,
      g_param_spec_uint ("preroll-queue-len", "preroll-queue-len",
          "Number of buffers to queue during preroll", 0, G_MAXUINT, 0,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

149
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_base_sink_set_clock);
Wim Taymans's avatar
Wim Taymans committed
150
  gstelement_class->change_state =
151
      GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
Wim Taymans's avatar
Wim Taymans committed
152 153 154 155

  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);
156
  klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
Wim Taymans's avatar
Wim Taymans committed
157 158 159
}

static GstCaps *
160
gst_base_sink_pad_getcaps (GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
161 162 163 164 165
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
  GstCaps *caps = NULL;

166
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
167
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
Wim Taymans's avatar
Wim Taymans committed
168 169 170 171
  if (bclass->get_caps)
    caps = bclass->get_caps (bsink);

  if (caps == NULL) {
Wim Taymans's avatar
Wim Taymans committed
172
    GstPadTemplate *pad_template;
Wim Taymans's avatar
Wim Taymans committed
173

Wim Taymans's avatar
Wim Taymans committed
174 175 176 177 178
    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
179
  }
180
  gst_object_unref (bsink);
Wim Taymans's avatar
Wim Taymans committed
181

Wim Taymans's avatar
Wim Taymans committed
182 183 184 185
  return caps;
}

static gboolean
186
gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
187 188 189 190 191
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
  gboolean res = FALSE;

192
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
193
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
194

Wim Taymans's avatar
Wim Taymans committed
195 196 197
  if (bclass->set_caps)
    res = bclass->set_caps (bsink, caps);

198 199
  gst_object_unref (bsink);

Wim Taymans's avatar
Wim Taymans committed
200 201 202
  return res;
}

203
static GstFlowReturn
204
gst_base_sink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
205
    GstCaps * caps, GstBuffer ** buf)
Wim Taymans's avatar
Wim Taymans committed
206 207 208
{
  GstBaseSinkClass *bclass;
  GstBaseSink *bsink;
209
  GstFlowReturn result = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
210

211
  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
212
  bclass = GST_BASE_SINK_GET_CLASS (bsink);
213

Wim Taymans's avatar
Wim Taymans committed
214
  if (bclass->buffer_alloc)
215 216 217
    result = bclass->buffer_alloc (bsink, offset, size, caps, buf);
  else
    *buf = NULL;
Wim Taymans's avatar
Wim Taymans committed
218

219 220
  gst_object_unref (bsink);

221
  return result;
Wim Taymans's avatar
Wim Taymans committed
222 223 224
}

static void
225
gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
Wim Taymans's avatar
Wim Taymans committed
226
{
Wim Taymans's avatar
Wim Taymans committed
227 228 229 230 231
  GstPadTemplate *pad_template;

  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
232

Wim Taymans's avatar
Wim Taymans committed
233
  basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
Wim Taymans's avatar
Wim Taymans committed
234 235

  gst_pad_set_getcaps_function (basesink->sinkpad,
236
      GST_DEBUG_FUNCPTR (gst_base_sink_pad_getcaps));
Wim Taymans's avatar
Wim Taymans committed
237
  gst_pad_set_setcaps_function (basesink->sinkpad,
238
      GST_DEBUG_FUNCPTR (gst_base_sink_pad_setcaps));
Wim Taymans's avatar
Wim Taymans committed
239
  gst_pad_set_bufferalloc_function (basesink->sinkpad,
240
      GST_DEBUG_FUNCPTR (gst_base_sink_pad_buffer_alloc));
Wim Taymans's avatar
Wim Taymans committed
241 242 243
  gst_element_add_pad (GST_ELEMENT (basesink), basesink->sinkpad);

  basesink->pad_mode = GST_ACTIVATE_NONE;
244
  GST_PAD_TASK (basesink->sinkpad) = NULL;
245
  basesink->preroll_queue = g_queue_new ();
246 247

  GST_FLAG_SET (basesink, GST_ELEMENT_IS_SINK);
Wim Taymans's avatar
Wim Taymans committed
248 249
}

250
static void
251
gst_base_sink_finalize (GObject * object)
252 253 254
{
  GstBaseSink *basesink;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
255
  basesink = GST_BASE_SINK (object);
256 257 258 259 260 261

  g_queue_free (basesink->preroll_queue);

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

Wim Taymans's avatar
Wim Taymans committed
262
static void
263
gst_base_sink_set_pad_functions (GstBaseSink * this, GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
264
{
265
  gst_pad_set_activatepush_function (pad,
266
      GST_DEBUG_FUNCPTR (gst_base_sink_activate_push));
267
  gst_pad_set_activatepull_function (pad,
268 269
      GST_DEBUG_FUNCPTR (gst_base_sink_activate_pull));
  gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_base_sink_event));
Wim Taymans's avatar
Wim Taymans committed
270 271

  if (this->has_chain)
272
    gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_base_sink_chain));
Wim Taymans's avatar
Wim Taymans committed
273 274 275 276 277
  else
    gst_pad_set_chain_function (pad, NULL);
}

static void
278
gst_base_sink_set_all_pad_functions (GstBaseSink * this)
Wim Taymans's avatar
Wim Taymans committed
279 280 281 282
{
  GList *l;

  for (l = GST_ELEMENT_PADS (this); l; l = l->next)
283
    gst_base_sink_set_pad_functions (this, (GstPad *) l->data);
Wim Taymans's avatar
Wim Taymans committed
284 285 286
}

static void
287
gst_base_sink_set_clock (GstElement * element, GstClock * clock)
Wim Taymans's avatar
Wim Taymans committed
288 289 290
{
  GstBaseSink *sink;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
291
  sink = GST_BASE_SINK (element);
Wim Taymans's avatar
Wim Taymans committed
292 293 294 295 296

  sink->clock = clock;
}

static void
297
gst_base_sink_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
298 299 300 301
    const GValue * value, GParamSpec * pspec)
{
  GstBaseSink *sink;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302
  sink = GST_BASE_SINK (object);
Wim Taymans's avatar
Wim Taymans committed
303 304 305

  switch (prop_id) {
    case PROP_HAS_LOOP:
306
      GST_LOCK (sink);
Wim Taymans's avatar
Wim Taymans committed
307
      sink->has_loop = g_value_get_boolean (value);
308
      gst_base_sink_set_all_pad_functions (sink);
309
      GST_UNLOCK (sink);
Wim Taymans's avatar
Wim Taymans committed
310 311
      break;
    case PROP_HAS_CHAIN:
312
      GST_LOCK (sink);
Wim Taymans's avatar
Wim Taymans committed
313
      sink->has_chain = g_value_get_boolean (value);
314
      gst_base_sink_set_all_pad_functions (sink);
315
      GST_UNLOCK (sink);
Wim Taymans's avatar
Wim Taymans committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329
      break;
    case PROP_PREROLL_QUEUE_LEN:
      /* preroll lock necessary to serialize with finish_preroll */
      GST_PREROLL_LOCK (sink->sinkpad);
      sink->preroll_queue_max_len = g_value_get_uint (value);
      GST_PREROLL_UNLOCK (sink->sinkpad);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
330
gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
Wim Taymans's avatar
Wim Taymans committed
331 332 333 334
    GParamSpec * pspec)
{
  GstBaseSink *sink;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
335
  sink = GST_BASE_SINK (object);
Wim Taymans's avatar
Wim Taymans committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

  GST_LOCK (sink);
  switch (prop_id) {
    case PROP_HAS_LOOP:
      g_value_set_boolean (value, sink->has_loop);
      break;
    case PROP_HAS_CHAIN:
      g_value_set_boolean (value, sink->has_chain);
      break;
    case PROP_PREROLL_QUEUE_LEN:
      g_value_set_uint (value, sink->preroll_queue_max_len);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
  GST_UNLOCK (sink);
}

static GstCaps *
gst_base_sink_get_caps (GstBaseSink * sink)
{
  return NULL;
}

static gboolean
gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
{
  return TRUE;
}

367
static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
368
gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size,
369
    GstCaps * caps, GstBuffer ** buf)
Wim Taymans's avatar
Wim Taymans committed
370
{
371 372
  *buf = NULL;
  return GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
373 374 375
}

/* with PREROLL_LOCK */
376
static GstFlowReturn
377
gst_base_sink_preroll_queue_empty (GstBaseSink * basesink, GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
378
{
379
  GstMiniObject *obj;
Wim Taymans's avatar
Wim Taymans committed
380
  GQueue *q = basesink->preroll_queue;
381 382 383
  GstFlowReturn ret;

  ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
384 385

  if (q) {
386 387
    GST_DEBUG ("emptying queue");
    while ((obj = g_queue_pop_head (q))) {
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
      gboolean is_buffer;

      is_buffer = GST_IS_BUFFER (obj);
      if (is_buffer) {
        basesink->preroll_queued--;
        basesink->buffers_queued--;
      } else {
        switch (GST_EVENT_TYPE (obj)) {
          case GST_EVENT_EOS:
            basesink->preroll_queued--;
            break;
          default:
            break;
        }
        basesink->events_queued--;
      }
404 405 406 407 408
      /* we release the preroll lock while pushing so that we
       * can still flush it while blocking on the clock or
       * inside the element. */
      GST_PREROLL_UNLOCK (pad);

409
      if (is_buffer) {
410
        GST_DEBUG ("popped buffer %p", obj);
411
        ret = gst_base_sink_handle_buffer (basesink, GST_BUFFER (obj));
412
      } else {
413
        GST_DEBUG ("popped event %p", obj);
414
        gst_base_sink_handle_event (basesink, GST_EVENT (obj));
415 416 417 418
        ret = GST_FLOW_OK;
      }

      GST_PREROLL_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
419
    }
420
    GST_DEBUG ("queue empty");
Wim Taymans's avatar
Wim Taymans committed
421
  }
422
  return ret;
Wim Taymans's avatar
Wim Taymans committed
423 424 425 426
}

/* with PREROLL_LOCK */
static void
427
gst_base_sink_preroll_queue_flush (GstBaseSink * basesink, GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
428
{
429
  GstMiniObject *obj;
Wim Taymans's avatar
Wim Taymans committed
430 431
  GQueue *q = basesink->preroll_queue;

432
  GST_DEBUG ("flushing queue %p", basesink);
Wim Taymans's avatar
Wim Taymans committed
433
  if (q) {
434
    while ((obj = g_queue_pop_head (q))) {
435
      GST_DEBUG ("popped %p", obj);
436
      gst_mini_object_unref (obj);
Wim Taymans's avatar
Wim Taymans committed
437 438
    }
  }
439 440
  /* we can't have EOS anymore now */
  basesink->eos = FALSE;
441 442 443
  basesink->preroll_queued = 0;
  basesink->buffers_queued = 0;
  basesink->events_queued = 0;
444
  basesink->have_preroll = FALSE;
445 446
  /* and signal any waiters now */
  GST_PREROLL_SIGNAL (pad);
Wim Taymans's avatar
Wim Taymans committed
447 448 449
}

/* with STREAM_LOCK */
450
static GstFlowReturn
451
gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad,
452
    GstMiniObject * obj)
Wim Taymans's avatar
Wim Taymans committed
453
{
454 455
  gint length;
  gboolean have_event;
Wim Taymans's avatar
Wim Taymans committed
456 457

  GST_PREROLL_LOCK (pad);
458
  /* push object on the queue */
459
  GST_DEBUG ("push on queue %p", basesink, obj);
460 461 462
  g_queue_push_tail (basesink->preroll_queue, obj);

  have_event = GST_IS_EVENT (obj);
463
  if (have_event) {
464 465
    GstEvent *event = GST_EVENT (obj);

466 467 468 469 470
    switch (GST_EVENT_TYPE (obj)) {
      case GST_EVENT_EOS:
        basesink->preroll_queued++;
        basesink->eos = TRUE;
        break;
Wim Taymans's avatar
Wim Taymans committed
471 472 473 474 475 476
      case GST_EVENT_NEWSEGMENT:
      {
        GstFormat format;
        gdouble rate;

        /* the newsegment event is needed to bring the buffer timestamps to the
477
         * stream time */
Wim Taymans's avatar
Wim Taymans committed
478 479 480 481
        gst_event_parse_newsegment (event, &rate, &format,
            &basesink->discont_start, &basesink->discont_stop, NULL);

        if (format != GST_FORMAT_TIME) {
482
          /* this means this sink will not be able to sync to the clock */
483 484 485 486 487 488 489 490 491
          basesink->discont_start = 0;
          basesink->discont_stop = 0;
        }
        basesink->have_discont = TRUE;

        GST_DEBUG ("received DISCONT %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
            GST_TIME_ARGS (basesink->discont_start),
            GST_TIME_ARGS (basesink->discont_stop));
        break;
Wim Taymans's avatar
Wim Taymans committed
492
      }
493 494 495 496 497
      default:
        break;
    }
    basesink->events_queued++;
  } else {
498 499 500 501 502
    if (!basesink->have_discont) {
      GST_ELEMENT_ERROR (basesink, STREAM, STOPPED,
          ("received buffer without a discont"),
          ("received buffer without a discont"));
    }
503 504
    basesink->preroll_queued++;
    basesink->buffers_queued++;
505
  }
506 507 508
  GST_DEBUG ("now %d preroll, %d buffers, %d events on queue",
      basesink->preroll_queued,
      basesink->buffers_queued, basesink->events_queued);
509 510

  /* check if we are prerolling */
Wim Taymans's avatar
Wim Taymans committed
511 512 513
  if (!basesink->need_preroll)
    goto no_preroll;

514 515
  /* there is a buffer queued */
  if (basesink->buffers_queued == 1) {
516 517 518 519 520
    GST_DEBUG ("do preroll %p", obj);

    /* if it's a buffer, we need to call the preroll method */
    if (GST_IS_BUFFER (obj)) {
      GstBaseSinkClass *bclass;
521
      GstFlowReturn pres;
522

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
523
      bclass = GST_BASE_SINK_GET_CLASS (basesink);
524
      if (bclass->preroll)
525 526 527
        if ((pres =
                bclass->preroll (basesink, GST_BUFFER (obj))) != GST_FLOW_OK)
          goto preroll_failed;
528 529
    }
  }
530 531
  length = basesink->preroll_queued;
  GST_DEBUG ("prerolled length %d", length);
532

533
  if (length == 1) {
534
    gint t;
535

536 537 538 539 540 541 542 543
    basesink->have_preroll = TRUE;
    /* we are prerolling */
    GST_PREROLL_UNLOCK (pad);

    /* have to release STREAM_LOCK as we cannot take the STATE_LOCK
     * inside the STREAM_LOCK */
    t = GST_STREAM_UNLOCK_FULL (pad);
    GST_DEBUG ("released stream lock %d times", t);
544
    if (t <= 0) {
545 546 547
      GST_WARNING ("STREAM_LOCK should have been locked !!");
      g_warning ("STREAM_LOCK should have been locked !!");
    }
548

549 550 551 552 553
    /* now we commit our state */
    GST_STATE_LOCK (basesink);
    GST_DEBUG ("commit state %p >", basesink);
    gst_element_commit_state (GST_ELEMENT (basesink));
    GST_STATE_UNLOCK (basesink);
Wim Taymans's avatar
Wim Taymans committed
554

555 556 557 558
    /* reacquire stream lock, pad could be flushing now */
    /* FIXME in glib, if t==0, the lock is still taken... hmmm */
    if (t > 0)
      GST_STREAM_LOCK_FULL (pad, t);
559

560 561
    /* and wait if needed */
    GST_PREROLL_LOCK (pad);
562

563 564 565 566
    GST_LOCK (pad);
    if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
      goto flushing;
    GST_UNLOCK (pad);
567

568 569 570 571 572 573
    /* it is possible that the application set the state to PLAYING
     * now in which case we don't need to block anymore. */
    if (!basesink->need_preroll)
      goto no_preroll;

    length = basesink->preroll_queued;
574 575

    g_assert (length == 1);
576
  }
Wim Taymans's avatar
Wim Taymans committed
577

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
  /* see if we need to block now. We cannot block on events, only
   * on buffers, the reason is that events can be sent from the
   * application thread and we don't want to block there. */
  if (length > basesink->preroll_queue_max_len && !have_event) {
    /* block until the state changes, or we get a flush, or something */
    GST_DEBUG ("element %s waiting to finish preroll",
        GST_ELEMENT_NAME (basesink));
    GST_PREROLL_WAIT (pad);
    GST_DEBUG ("done preroll");
  }
  GST_LOCK (pad);
  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
    goto flushing;
  GST_UNLOCK (pad);

593 594
  GST_PREROLL_UNLOCK (pad);

595
  return GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
596 597 598

no_preroll:
  {
599 600 601
    GstFlowReturn ret;

    GST_DEBUG ("no preroll needed");
Wim Taymans's avatar
Wim Taymans committed
602 603
    /* maybe it was another sink that blocked in preroll, need to check for
       buffers to drain */
604
    basesink->have_preroll = FALSE;
605
    ret = gst_base_sink_preroll_queue_empty (basesink, pad);
Wim Taymans's avatar
Wim Taymans committed
606
    GST_PREROLL_UNLOCK (pad);
607 608

    return ret;
Wim Taymans's avatar
Wim Taymans committed
609
  }
610
flushing:
Wim Taymans's avatar
Wim Taymans committed
611
  {
612
    GST_UNLOCK (pad);
613
    gst_base_sink_preroll_queue_flush (basesink, pad);
614
    GST_PREROLL_UNLOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
615
    GST_DEBUG ("pad is flushing");
616
    return GST_FLOW_WRONG_STATE;
Wim Taymans's avatar
Wim Taymans committed
617
  }
618 619
preroll_failed:
  {
620
    gint t;
621 622 623 624 625 626 627 628 629

    GST_DEBUG ("preroll failed");
    gst_base_sink_preroll_queue_flush (basesink, pad);
    GST_PREROLL_UNLOCK (pad);

    /* have to release STREAM_LOCK as we cannot take the STATE_LOCK
     * inside the STREAM_LOCK */
    t = GST_STREAM_UNLOCK_FULL (pad);
    GST_DEBUG ("released stream lock %d times", t);
630
    if (t <= 0) {
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
      GST_WARNING ("STREAM_LOCK should have been locked !!");
      g_warning ("STREAM_LOCK should have been locked !!");
    }

    /* now we abort our state */
    GST_STATE_LOCK (basesink);
    GST_DEBUG ("abort state %p >", basesink);
    gst_element_abort_state (GST_ELEMENT (basesink));
    GST_STATE_UNLOCK (basesink);

    /* reacquire stream lock, pad could be flushing now */
    if (t > 0)
      GST_STREAM_LOCK_FULL (pad, t);

    return GST_FLOW_ERROR;
  }
Wim Taymans's avatar
Wim Taymans committed
647 648 649
}

static gboolean
650
gst_base_sink_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
651 652 653 654 655
{
  GstBaseSink *basesink;
  gboolean result = TRUE;
  GstBaseSinkClass *bclass;

656
  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
657

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
658
  bclass = GST_BASE_SINK_GET_CLASS (basesink);
Wim Taymans's avatar
Wim Taymans committed
659

660
  GST_DEBUG ("event %p", event);
Wim Taymans's avatar
Wim Taymans committed
661 662 663 664

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_EOS:
    {
665
      GstFlowReturn ret;
Wim Taymans's avatar
Wim Taymans committed
666 667

      GST_STREAM_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
668
      /* EOS also finishes the preroll */
669 670
      ret =
          gst_base_sink_handle_object (basesink, pad, GST_MINI_OBJECT (event));
Wim Taymans's avatar
Wim Taymans committed
671 672 673
      GST_STREAM_UNLOCK (pad);
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
674
    case GST_EVENT_NEWSEGMENT:
675 676 677
    {
      GstFlowReturn ret;

Wim Taymans's avatar
Wim Taymans committed
678
      GST_STREAM_LOCK (pad);
679 680
      ret =
          gst_base_sink_handle_object (basesink, pad, GST_MINI_OBJECT (event));
Wim Taymans's avatar
Wim Taymans committed
681 682
      GST_STREAM_UNLOCK (pad);
      break;
683
    }
Wim Taymans's avatar
Wim Taymans committed
684
    case GST_EVENT_FLUSH_START:
Wim Taymans's avatar
Wim Taymans committed
685 686
      /* make sure we are not blocked on the clock also clear any pending
       * eos state. */
687 688 689
      if (bclass->event)
        bclass->event (basesink, event);

Wim Taymans's avatar
Wim Taymans committed
690 691 692 693 694 695 696 697 698
      GST_PREROLL_LOCK (pad);
      /* we need preroll after the flush */
      basesink->need_preroll = TRUE;
      /* unlock from a possible state change/preroll */
      gst_base_sink_preroll_queue_flush (basesink, pad);

      GST_LOCK (basesink);
      if (basesink->clock_id) {
        gst_clock_id_unschedule (basesink->clock_id);
Wim Taymans's avatar
Wim Taymans committed
699
      }
Wim Taymans's avatar
Wim Taymans committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
      GST_UNLOCK (basesink);
      GST_PREROLL_UNLOCK (pad);

      /* and we need to commit our state again on the next
       * prerolled buffer */
      GST_STATE_LOCK (basesink);
      GST_STREAM_LOCK (pad);
      gst_element_lost_state (GST_ELEMENT (basesink));
      GST_STREAM_UNLOCK (pad);
      GST_STATE_UNLOCK (basesink);
      GST_DEBUG ("event unref %p %p", basesink, event);
      gst_event_unref (event);
      break;
    case GST_EVENT_FLUSH_STOP:
      if (bclass->event)
        bclass->event (basesink, event);

      /* now we are completely unblocked and the _chain method
       * will return */
      GST_STREAM_LOCK (pad);
      GST_STREAM_UNLOCK (pad);

722 723
      GST_DEBUG ("event unref %p %p", basesink, event);
      gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
724 725
      break;
    default:
726
      gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
727 728
      break;
  }
729
  gst_object_unref (basesink);
Wim Taymans's avatar
Wim Taymans committed
730 731 732 733

  return result;
}

734
/* default implementation to calculate the start and end
735
 * timestamps on a buffer, subclasses can override
736
 */
Wim Taymans's avatar
Wim Taymans committed
737
static void
738
gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
739 740 741 742 743 744
    GstClockTime * start, GstClockTime * end)
{
  GstClockTime timestamp, duration;

  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
745 746
    GstClockTimeDiff diff;

747 748
    /* bring timestamp to stream time using last
     * discont offset. */
749 750 751
    if ((diff = timestamp - basesink->discont_start) < 0)
      goto too_late;

752
    /* get duration to calculate end time */
Wim Taymans's avatar
Wim Taymans committed
753 754
    duration = GST_BUFFER_DURATION (buffer);
    if (GST_CLOCK_TIME_IS_VALID (duration)) {
755
      *end = diff + duration;
Wim Taymans's avatar
Wim Taymans committed
756
    }
757 758 759 760 761 762 763 764
    *start = diff;
  }
  return;

too_late:
  {
    *start = GST_CLOCK_TIME_NONE;
    *end = GST_CLOCK_TIME_NONE;
Wim Taymans's avatar
Wim Taymans committed
765 766 767
  }
}

768 769 770 771 772 773 774 775 776
/* perform synchronisation on a buffer
 * 
 * 1) check if we have a clock, if not, do nothing
 * 2) calculate the start and end time of the buffer
 * 3) create a single shot notification to wait on
 *    the clock, save the entry so we can unlock it
 * 4) wait on the clock, this blocks
 * 5) unref the clockid again
 */
777
static gboolean
778
gst_base_sink_do_sync (GstBaseSink * basesink, GstBuffer * buffer)
Wim Taymans's avatar
Wim Taymans committed
779
{
780 781
  gboolean result = TRUE;

Wim Taymans's avatar
Wim Taymans committed
782 783 784 785
  if (basesink->clock) {
    GstClockTime start, end;
    GstBaseSinkClass *bclass;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
786
    bclass = GST_BASE_SINK_GET_CLASS (basesink);
Wim Taymans's avatar
Wim Taymans committed
787 788 789 790
    start = end = -1;
    if (bclass->get_times)
      bclass->get_times (basesink, buffer, &start, &end);

791 792 793
    GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
        ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));

Wim Taymans's avatar
Wim Taymans committed
794
    if (GST_CLOCK_TIME_IS_VALID (start)) {
795
      GstClockReturn ret;
796
      GstClockTime base_time;
797

Wim Taymans's avatar
Wim Taymans committed
798
      GST_LOCK (basesink);
799 800 801 802 803 804
      base_time = GST_ELEMENT (basesink)->base_time;

      GST_LOG_OBJECT (basesink,
          "waiting for clock, base time %" GST_TIME_FORMAT,
          GST_TIME_ARGS (base_time));
      /* save clock id so that we can unlock it if needed */
Wim Taymans's avatar
Wim Taymans committed
805
      basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
806
          start + base_time);
Wim Taymans's avatar
Wim Taymans committed
807 808 809 810 811 812 813 814 815 816 817 818 819
      basesink->end_time = end;
      GST_UNLOCK (basesink);

      ret = gst_clock_id_wait (basesink->clock_id, NULL);

      GST_LOCK (basesink);
      if (basesink->clock_id) {
        gst_clock_id_unref (basesink->clock_id);
        basesink->clock_id = NULL;
      }
      GST_UNLOCK (basesink);

      GST_LOG_OBJECT (basesink, "clock entry done: %d", ret);
820 821
      if (ret == GST_CLOCK_UNSCHEDULED)
        result = FALSE;
Wim Taymans's avatar
Wim Taymans committed
822 823
    }
  }
824 825 826 827 828 829 830 831 832 833
  return result;
}


/* handle an event
 *
 * 2) render the event
 * 3) unref the event
 */
static inline gboolean
834
gst_base_sink_handle_event (GstBaseSink * basesink, GstEvent * event)
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
{
  GstBaseSinkClass *bclass;
  gboolean ret;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_EOS:
      GST_LOCK (basesink);
      if (basesink->clock) {
        /* wait for last buffer to finish if we have a valid end time */
        if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) {
          basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
              basesink->end_time + GST_ELEMENT (basesink)->base_time);
          GST_UNLOCK (basesink);

          gst_clock_id_wait (basesink->clock_id, NULL);

          GST_LOCK (basesink);
          if (basesink->clock_id) {
            gst_clock_id_unref (basesink->clock_id);
            basesink->clock_id = NULL;
          }
          basesink->end_time = GST_CLOCK_TIME_NONE;
        }
      }
      GST_UNLOCK (basesink);
      break;
    default:
      break;
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
865
  bclass = GST_BASE_SINK_GET_CLASS (basesink);
866 867 868 869 870 871 872 873 874 875 876
  if (bclass->event)
    ret = bclass->event (basesink, event);
  else
    ret = TRUE;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_EOS:
      GST_PREROLL_LOCK (basesink->sinkpad);
      /* if we are still EOS, we can post the EOS message */
      if (basesink->eos) {
        /* ok, now we can post the message */
877
        GST_DEBUG_OBJECT (basesink, "Now posting EOS");
878 879 880 881 882 883 884 885 886 887 888 889 890
        gst_element_post_message (GST_ELEMENT (basesink),
            gst_message_new_eos (GST_OBJECT (basesink)));
      }
      GST_PREROLL_UNLOCK (basesink->sinkpad);
      break;
    default:
      break;
  }

  GST_DEBUG ("event unref %p %p", basesink, event);
  gst_event_unref (event);

  return ret;
Wim Taymans's avatar
Wim Taymans committed
891 892
}

893 894 895 896 897 898
/* handle a buffer
 *
 * 1) first sync on the buffer
 * 2) render the buffer
 * 3) unref the buffer
 */
899
static inline GstFlowReturn
900
gst_base_sink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf)
Wim Taymans's avatar
Wim Taymans committed
901 902
{
  GstBaseSinkClass *bclass;
903
  GstFlowReturn ret;
Wim Taymans's avatar
Wim Taymans committed
904

905
  gst_base_sink_do_sync (basesink, buf);
Wim Taymans's avatar
Wim Taymans committed
906

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
907
  bclass = GST_BASE_SINK_GET_CLASS (basesink);
Wim Taymans's avatar
Wim Taymans committed
908
  if (bclass->render)
909 910 911
    ret = bclass->render (basesink, buf);
  else
    ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
912

913
  GST_DEBUG ("buffer unref after render %p", basesink, buf);
Wim Taymans's avatar
Wim Taymans committed
914
  gst_buffer_unref (buf);
915 916

  return ret;
Wim Taymans's avatar
Wim Taymans committed
917 918 919
}

static GstFlowReturn
920
gst_base_sink_chain (GstPad * pad, GstBuffer * buf)
Wim Taymans's avatar
Wim Taymans committed
921 922
{
  GstBaseSink *basesink;
923
  GstFlowReturn result;
Wim Taymans's avatar
Wim Taymans committed
924

925
  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
926

927
  result = gst_base_sink_handle_object (basesink, pad, GST_MINI_OBJECT (buf));
Wim Taymans's avatar
Wim Taymans committed
928

929 930
  gst_object_unref (basesink);

Wim Taymans's avatar
Wim Taymans committed
931 932 933
  return result;
}

934 935
/* FIXME, not all sinks can operate in pull mode 
 */
Wim Taymans's avatar
Wim Taymans committed
936
static void
937
gst_base_sink_loop (GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
938 939 940 941 942
{
  GstBaseSink *basesink;
  GstBuffer *buf = NULL;
  GstFlowReturn result;

943
  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
944 945 946 947 948 949 950

  g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);

  result = gst_pad_pull_range (pad, basesink->offset, DEFAULT_SIZE, &buf);
  if (result != GST_FLOW_OK)
    goto paused;

951
  result = gst_base_sink_chain (pad, buf);
Wim Taymans's avatar
Wim Taymans committed
952 953 954
  if (result != GST_FLOW_OK)
    goto paused;

955 956
  gst_object_unref (basesink);

Wim Taymans's avatar
Wim Taymans committed
957 958 959 960
  /* default */
  return;

paused:
961
  {
962
    gst_object_unref (basesink);
963 964 965
    gst_pad_pause_task (pad);
    return;
  }
Wim Taymans's avatar
Wim Taymans committed
966 967 968
}

static gboolean
969
gst_base_sink_deactivate (GstBaseSink * basesink, GstPad * pad)
Wim Taymans's avatar
Wim Taymans committed
970 971
{
  gboolean result = FALSE;
Wim Taymans's avatar
Wim Taymans committed
972
  GstBaseSinkClass *bclass;
Wim Taymans's avatar
Wim Taymans committed
973

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
974
  bclass = GST_BASE_SINK_GET_CLASS (basesink);
Wim Taymans's avatar
Wim Taymans committed
975

976 977 978 979 980 981 982
  /* step 1, unblock clock sync (if any) or any other blocking thing */
  GST_PREROLL_LOCK (pad);
  GST_LOCK (basesink);
  if (basesink->clock_id) {
    gst_clock_id_unschedule (basesink->clock_id);
  }
  GST_UNLOCK (basesink);
Wim Taymans's avatar
Wim Taymans committed
983

984 985 986
  /* unlock any subclasses */
  if (bclass->unlock)
    bclass->unlock (basesink);
Wim Taymans's avatar
Wim Taymans committed
987

988 989
  /* flush out the data thread if it's locked in finish_preroll */
  basesink->need_preroll = FALSE;
990
  gst_base_sink_preroll_queue_flush (basesink, pad);
991
  GST_PREROLL_SIGNAL (pad);
992
  GST_PREROLL_UNLOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
993

994 995 996 997 998 999 1000
  /* step 2, make sure streaming finishes */
  result = gst_pad_stop_task (pad);

  return result;
}

static gboolean
1001
gst_base_sink_activate_push (GstPad * pad, gboolean active)
1002 1003 1004 1005
{
  gboolean result = FALSE;
  GstBaseSink *basesink;