gstbaseaudiosink.c 26.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2005 Wim Taymans <wim@fluendo.com>
 *
 * gstbaseaudiosink.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.
 */

Wim Taymans's avatar
Wim Taymans committed
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * SECTION:gstbaseaudiosink
 * @short_description: Base class for audio sinks
 * @see_also: #GstAudioSink, #GstRingBuffer.
 *
 * This is the base class for audio sinks. Subclasses need to implement the
 * ::create_ringbuffer vmethod. This base class will then take care of
 * writing samples to the ringbuffer, synchronisation, clipping and flushing.
 *
 * Last reviewed on 2006-09-27 (0.10.12)
 */

35
36
#include <string.h>

37
38
#include "gstbaseaudiosink.h"

39
40
GST_DEBUG_CATEGORY_STATIC (gst_base_audio_sink_debug);
#define GST_CAT_DEFAULT gst_base_audio_sink_debug
41
42
43
44
45
46
47
48

/* BaseAudioSink signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

49
/* we tollerate half a second diff before we start resyncing. This
50
 * should be enough to compensate for various rounding errors in the timestamp
51
52
53
 * and sample offset position. 
 * This is an emergency resync fallback since buffers marked as DISCONT will
 * always lock to the correct timestamp immediatly and buffers not marked as
54
 * DISCONT are contiguous by definition.
55
56
 */
#define DIFF_TOLERANCE  2
57

58
59
60
/* FIXME: 0.11, store the buffer_time and latency_time in nanoseconds */
#define DEFAULT_BUFFER_TIME     ((200 * GST_MSECOND) / GST_USECOND)
#define DEFAULT_LATENCY_TIME    ((10 * GST_MSECOND) / GST_USECOND)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61
#define DEFAULT_PROVIDE_CLOCK   TRUE
62

63
64
65
enum
{
  PROP_0,
66
67
  PROP_BUFFER_TIME,
  PROP_LATENCY_TIME,
68
  PROP_PROVIDE_CLOCK,
69
70
71
};

#define _do_init(bla) \
72
    GST_DEBUG_CATEGORY_INIT (gst_base_audio_sink_debug, "baseaudiosink", 0, "baseaudiosink element");
73

74
75
GST_BOILERPLATE_FULL (GstBaseAudioSink, gst_base_audio_sink, GstBaseSink,
    GST_TYPE_BASE_SINK, _do_init);
76

77
static void gst_base_audio_sink_dispose (GObject * object);
78

79
static void gst_base_audio_sink_set_property (GObject * object, guint prop_id,
80
    const GValue * value, GParamSpec * pspec);
81
static void gst_base_audio_sink_get_property (GObject * object, guint prop_id,
82
83
    GValue * value, GParamSpec * pspec);

84
85
static GstStateChangeReturn gst_base_audio_sink_async_play (GstBaseSink *
    basesink);
86
87
static GstStateChangeReturn gst_base_audio_sink_change_state (GstElement *
    element, GstStateChange transition);
88

89
static GstClock *gst_base_audio_sink_provide_clock (GstElement * elem);
90
static GstClockTime gst_base_audio_sink_get_time (GstClock * clock,
91
    GstBaseAudioSink * sink);
92
93
static void gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data,
    guint len, gpointer user_data);
94

95
static GstFlowReturn gst_base_audio_sink_preroll (GstBaseSink * bsink,
96
    GstBuffer * buffer);
97
static GstFlowReturn gst_base_audio_sink_render (GstBaseSink * bsink,
98
    GstBuffer * buffer);
99
100
101
static gboolean gst_base_audio_sink_event (GstBaseSink * bsink,
    GstEvent * event);
static void gst_base_audio_sink_get_times (GstBaseSink * bsink,
102
    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
103
104
static gboolean gst_base_audio_sink_setcaps (GstBaseSink * bsink,
    GstCaps * caps);
105

106
/* static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; */
107
108

static void
109
gst_base_audio_sink_base_init (gpointer g_class)
110
111
112
113
{
}

static void
114
gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass)
115
116
117
118
119
120
121
122
123
124
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseSinkClass *gstbasesink_class;

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

  gobject_class->set_property =
125
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_set_property);
126
  gobject_class->get_property =
127
128
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_get_property);
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_base_audio_sink_dispose);
129

130
  g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
131
132
      g_param_spec_int64 ("buffer-time", "Buffer Time",
          "Size of audio buffer in microseconds", 1,
133
          G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
134

135
  g_object_class_install_property (gobject_class, PROP_LATENCY_TIME,
136
137
      g_param_spec_int64 ("latency-time", "Latency Time",
          "Audio latency in microseconds", 1,
138
          G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));
139

140
  g_object_class_install_property (gobject_class, PROP_PROVIDE_CLOCK,
141
142
143
      g_param_spec_boolean ("provide-clock", "Provide Clock",
          "Provide a clock to be used as the global pipeline clock",
          DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE));
144
145

  gstelement_class->change_state =
146
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state);
147
148
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_provide_clock);
149

150
151
152
  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_sink_event);
  gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_base_audio_sink_preroll);
  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_base_audio_sink_render);
153
  gstbasesink_class->get_times =
154
155
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_get_times);
  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_sink_setcaps);
156
157
  gstbasesink_class->async_play =
      GST_DEBUG_FUNCPTR (gst_base_audio_sink_async_play);
158
159
160
}

static void
161
162
gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,
    GstBaseAudioSinkClass * g_class)
163
{
164
165
  baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosink->latency_time = DEFAULT_LATENCY_TIME;
166
  baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK;
167

168
  baseaudiosink->provided_clock = gst_audio_clock_new ("clock",
169
      (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);
170
171
}

172
static void
173
gst_base_audio_sink_dispose (GObject * object)
174
175
176
{
  GstBaseAudioSink *sink;

177
  sink = GST_BASE_AUDIO_SINK (object);
178

179
180
181
  if (sink->provided_clock)
    gst_object_unref (sink->provided_clock);
  sink->provided_clock = NULL;
182

183
184
185
186
  if (sink->ringbuffer) {
    gst_object_unparent (GST_OBJECT_CAST (sink->ringbuffer));
    sink->ringbuffer = NULL;
  }
187

188
189
190
  G_OBJECT_CLASS (parent_class)->dispose (object);
}

191
static GstClock *
192
gst_base_audio_sink_provide_clock (GstElement * elem)
193
194
{
  GstBaseAudioSink *sink;
195
  GstClock *clock;
196

197
  sink = GST_BASE_AUDIO_SINK (elem);
198

199
  /* we have no ringbuffer (must be NULL state) */
200
201
202
  if (sink->ringbuffer == NULL)
    goto wrong_state;

203
204
205
206
  if (!gst_ring_buffer_is_acquired (sink->ringbuffer))
    goto wrong_state;

  GST_OBJECT_LOCK (sink);
207
  if (!sink->provide_clock)
208
209
210
211
    goto clock_disabled;

  clock = GST_CLOCK_CAST (gst_object_ref (sink->provided_clock));
  GST_OBJECT_UNLOCK (sink);
212
213

  return clock;
214

215
  /* ERRORS */
216
217
218
219
220
221
222
223
224
225
226
wrong_state:
  {
    GST_DEBUG_OBJECT (sink, "ringbuffer not acquired");
    return NULL;
  }
clock_disabled:
  {
    GST_DEBUG_OBJECT (sink, "clock provide disabled");
    GST_OBJECT_UNLOCK (sink);
    return NULL;
  }
227
228
229
}

static GstClockTime
230
gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
231
{
232
233
  guint64 raw, samples;
  guint delay;
234
235
236
  GstClockTime result;

  if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
237
    return GST_CLOCK_TIME_NONE;
238

Wim Taymans's avatar
Wim Taymans committed
239
  /* our processed samples are always increasing */
240
241
242
243
244
245
246
247
248
249
  raw = samples = gst_ring_buffer_samples_done (sink->ringbuffer);

  /* the number of samples not yet processed, this is still queued in the
   * device (not played for playback). */
  delay = gst_ring_buffer_delay (sink->ringbuffer);

  if (G_LIKELY (samples >= delay))
    samples -= delay;
  else
    samples = 0;
250

251
252
  result = gst_util_uint64_scale_int (samples, GST_SECOND,
      sink->ringbuffer->spec.rate);
253

254
255
256
257
  GST_DEBUG_OBJECT (sink,
      "processed samples: raw %llu, delay %u, real %llu, time %"
      GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));

258
  return result;
259
260
261
}

static void
262
gst_base_audio_sink_set_property (GObject * object, guint prop_id,
263
264
265
266
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSink *sink;

267
  sink = GST_BASE_AUDIO_SINK (object);
268
269

  switch (prop_id) {
270
271
    case PROP_BUFFER_TIME:
      sink->buffer_time = g_value_get_int64 (value);
272
      break;
273
274
    case PROP_LATENCY_TIME:
      sink->latency_time = g_value_get_int64 (value);
275
      break;
276
    case PROP_PROVIDE_CLOCK:
277
      GST_OBJECT_LOCK (sink);
278
      sink->provide_clock = g_value_get_boolean (value);
279
      GST_OBJECT_UNLOCK (sink);
280
      break;
281
282
283
284
285
286
287
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
288
289
gst_base_audio_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
290
291
292
{
  GstBaseAudioSink *sink;

293
  sink = GST_BASE_AUDIO_SINK (object);
294
295

  switch (prop_id) {
296
297
    case PROP_BUFFER_TIME:
      g_value_set_int64 (value, sink->buffer_time);
298
      break;
299
300
    case PROP_LATENCY_TIME:
      g_value_set_int64 (value, sink->latency_time);
301
      break;
302
    case PROP_PROVIDE_CLOCK:
303
      GST_OBJECT_LOCK (sink);
304
      g_value_set_boolean (value, sink->provide_clock);
305
      GST_OBJECT_UNLOCK (sink);
306
      break;
307
308
309
310
311
312
313
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
314
gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
315
{
316
  GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (bsink);
317
318
319
320
  GstRingBufferSpec *spec;

  spec = &sink->ringbuffer->spec;

321
  GST_DEBUG_OBJECT (sink, "release old ringbuffer");
322

Wim Taymans's avatar
Wim Taymans committed
323
  /* release old ringbuffer */
324
  gst_ring_buffer_release (sink->ringbuffer);
325

326
  GST_DEBUG_OBJECT (sink, "parse caps");
327
328
329
330

  spec->buffer_time = sink->buffer_time;
  spec->latency_time = sink->latency_time;

Wim Taymans's avatar
Wim Taymans committed
331
  /* parse new caps */
332
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
333
    goto parse_error;
334

335
  gst_ring_buffer_debug_spec_buff (spec);
336

337
  GST_DEBUG_OBJECT (sink, "acquire new ringbuffer");
338

339
  if (!gst_ring_buffer_acquire (sink->ringbuffer, spec))
340
341
    goto acquire_error;

342
343
344
345
346
347
  /* calculate actual latency and buffer times. 
   * FIXME: In 0.11, store the latency_time internally in ns */
  spec->latency_time = gst_util_uint64_scale (spec->segsize,
      (GST_SECOND / GST_USECOND), spec->rate * spec->bytes_per_sample);

  spec->buffer_time = spec->segtotal * spec->latency_time;
348

349
  gst_ring_buffer_debug_spec_buff (spec);
350
351

  return TRUE;
352
353
354
355

  /* ERRORS */
parse_error:
  {
356
357
    GST_DEBUG_OBJECT (sink, "could not parse caps");
    GST_ELEMENT_ERROR (sink, STREAM, FORMAT,
358
        (NULL), ("cannot parse audio format."));
359
360
361
362
    return FALSE;
  }
acquire_error:
  {
363
    GST_DEBUG_OBJECT (sink, "could not acquire ringbuffer");
364
365
    return FALSE;
  }
366
367
368
}

static void
369
gst_base_audio_sink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
370
371
    GstClockTime * start, GstClockTime * end)
{
Wim Taymans's avatar
Wim Taymans committed
372
373
  /* our clock sync is a bit too much for the base class to handle so
   * we implement it ourselves. */
374
375
376
377
  *start = GST_CLOCK_TIME_NONE;
  *end = GST_CLOCK_TIME_NONE;
}

378
379
380
/* FIXME, this waits for the drain to happen but it cannot be
 * canceled.
 */
381
382
383
384
385
386
387
388
static gboolean
gst_base_audio_sink_drain (GstBaseAudioSink * sink)
{
  if (!sink->ringbuffer)
    return TRUE;
  if (!sink->ringbuffer->spec.rate)
    return TRUE;

389
390
391
392
393
394
  /* need to start playback before we can drain, but only when
   * we have successfully negotiated a format and thus aqcuired the
   * ringbuffer. */
  if (gst_ring_buffer_is_acquired (sink->ringbuffer))
    gst_ring_buffer_start (sink->ringbuffer);

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  if (sink->next_sample != -1) {
    GstClockTime time;
    GstClock *clock;

    time =
        gst_util_uint64_scale_int (sink->next_sample, GST_SECOND,
        sink->ringbuffer->spec.rate);

    GST_OBJECT_LOCK (sink);
    if ((clock = GST_ELEMENT_CLOCK (sink)) != NULL) {
      GstClockID id = gst_clock_new_single_shot_id (clock, time);

      GST_OBJECT_UNLOCK (sink);

      GST_DEBUG_OBJECT (sink, "waiting for last sample to play");
      gst_clock_id_wait (id, NULL);
411
412

      gst_clock_id_unref (id);
413
414
415
416
417
418
419
420
      sink->next_sample = -1;
    } else {
      GST_OBJECT_UNLOCK (sink);
    }
  }
  return TRUE;
}

421
static gboolean
422
gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event)
423
{
424
  GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (bsink);
425
426

  switch (GST_EVENT_TYPE (event)) {
427
    case GST_EVENT_FLUSH_START:
428
429
      if (sink->ringbuffer)
        gst_ring_buffer_set_flushing (sink->ringbuffer, TRUE);
430
431
      break;
    case GST_EVENT_FLUSH_STOP:
432
433
      /* always resync on sample after a flush */
      sink->next_sample = -1;
434
435
      if (sink->ringbuffer)
        gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
436
      break;
437
    case GST_EVENT_EOS:
438
      /* now wait till we played everything */
439
      gst_base_audio_sink_drain (sink);
440
      break;
441
442
443
    default:
      break;
  }
444
  return TRUE;
445
446
447
}

static GstFlowReturn
448
gst_base_audio_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
449
{
450
  GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (bsink);
Wim Taymans's avatar
Wim Taymans committed
451

452
  if (!gst_ring_buffer_is_acquired (sink->ringbuffer))
Wim Taymans's avatar
Wim Taymans committed
453
454
    goto wrong_state;

Wim Taymans's avatar
Wim Taymans committed
455
456
457
  /* we don't really do anything when prerolling. We could make a
   * property to play this buffer to have some sort of scrubbing
   * support. */
458
  return GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
459
460
461

wrong_state:
  {
462
    GST_DEBUG_OBJECT (sink, "ringbuffer in wrong state");
463
    GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated."));
464
    return GST_FLOW_NOT_NEGOTIATED;
Wim Taymans's avatar
Wim Taymans committed
465
  }
466
467
}

468
469
470
471
472
473
474
475
476
static guint64
gst_base_audio_sink_get_offset (GstBaseAudioSink * sink)
{
  guint64 sample;
  gint writeseg, segdone, sps;
  gint diff;

  /* assume we can append to the previous sample */
  sample = sink->next_sample;
477
478
479
  /* no previous sample, try to insert at position 0 */
  if (sample == -1)
    sample = 0;
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500

  sps = sink->ringbuffer->samples_per_seg;

  /* figure out the segment and the offset inside the segment where
   * the sample should be written. */
  writeseg = sample / sps;

  /* get the currently processed segment */
  segdone = g_atomic_int_get (&sink->ringbuffer->segdone)
      - sink->ringbuffer->segbase;

  /* see how far away it is from the write segment */
  diff = writeseg - segdone;
  if (diff < 0) {
    /* sample would be dropped, position to next playable position */
    sample = (segdone + 1) * sps;
  }

  return sample;
}

501
static GstFlowReturn
502
gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
503
{
Wim Taymans's avatar
Wim Taymans committed
504
  guint64 render_offset, in_offset;
505
  GstClockTime time, stop, render_time, duration;
Wim Taymans's avatar
Wim Taymans committed
506
507
  GstBaseAudioSink *sink;
  GstRingBuffer *ringbuf;
508
  gint64 diff, ctime, cstop;
509
510
  guint8 *data;
  guint size;
511
  guint samples, written;
Wim Taymans's avatar
Wim Taymans committed
512
  gint bps;
513
  gdouble crate = 1.0;
514
515
  GstClockTime crate_num;
  GstClockTime crate_denom;
Wim Taymans's avatar
Wim Taymans committed
516
  GstClockTime cinternal, cexternal;
Wim Taymans's avatar
Wim Taymans committed
517
518
519
520

  sink = GST_BASE_AUDIO_SINK (bsink);

  ringbuf = sink->ringbuffer;
521

Wim Taymans's avatar
Wim Taymans committed
522
  /* can't do anything when we don't have the device */
523
  if (G_UNLIKELY (!gst_ring_buffer_is_acquired (ringbuf)))
524
    goto wrong_state;
525

Wim Taymans's avatar
Wim Taymans committed
526
527
528
  bps = ringbuf->spec.bytes_per_sample;

  size = GST_BUFFER_SIZE (buf);
529
  if (G_UNLIKELY (size % bps) != 0)
Wim Taymans's avatar
Wim Taymans committed
530
531
532
533
    goto wrong_size;

  samples = size / bps;

Wim Taymans's avatar
Wim Taymans committed
534
535
  in_offset = GST_BUFFER_OFFSET (buf);
  time = GST_BUFFER_TIMESTAMP (buf);
Wim Taymans's avatar
Wim Taymans committed
536
  duration = GST_BUFFER_DURATION (buf);
537
  data = GST_BUFFER_DATA (buf);
Wim Taymans's avatar
Wim Taymans committed
538

539
540
  GST_DEBUG_OBJECT (sink,
      "time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT,
Wim Taymans's avatar
Wim Taymans committed
541
      GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->segment.start));
Wim Taymans's avatar
Wim Taymans committed
542

543
544
545
  /* if not valid timestamp or we don't need to sync, try to play
   * sample ASAP */
  if (!GST_CLOCK_TIME_IS_VALID (time) || !bsink->sync) {
546
    render_offset = gst_base_audio_sink_get_offset (sink);
547
548
549
550
    stop = -1;
    GST_DEBUG_OBJECT (sink,
        "Buffer of size %u has no time. Using render_offset=%" G_GUINT64_FORMAT,
        GST_BUFFER_SIZE (buf), render_offset);
Wim Taymans's avatar
Wim Taymans committed
551
552
553
    goto no_sync;
  }

Wim Taymans's avatar
Wim Taymans committed
554
  /* samples should be rendered based on their timestamp. All samples
555
556
557
   * arriving before the segment.start or after segment.stop are to be 
   * thrown away. All samples should also be clipped to the segment 
   * boundaries */
558
559
  /* let's calc stop based on the number of samples in the buffer instead
   * of trusting the DURATION */
560
561
562
563
564
  stop =
      time + gst_util_uint64_scale_int (samples, GST_SECOND,
      ringbuf->spec.rate);
  if (!gst_segment_clip (&bsink->segment, GST_FORMAT_TIME, time, stop, &ctime,
          &cstop))
Wim Taymans's avatar
Wim Taymans committed
565
    goto out_of_segment;
Wim Taymans's avatar
Wim Taymans committed
566

567
568
569
  /* see if some clipping happened */
  diff = ctime - time;
  if (diff > 0) {
570
    /* bring clipped time to samples */
571
572
573
574
    diff = gst_util_uint64_scale_int (diff, ringbuf->spec.rate, GST_SECOND);
    GST_DEBUG_OBJECT (sink, "clipping start to %" GST_TIME_FORMAT " %"
        G_GUINT64_FORMAT " samples", GST_TIME_ARGS (ctime), diff);
    samples -= diff;
575
    data += diff * bps;
576
577
578
579
    time = ctime;
  }
  diff = stop - cstop;
  if (diff > 0) {
580
    /* bring clipped time to samples */
581
582
583
584
585
586
587
    diff = gst_util_uint64_scale_int (diff, ringbuf->spec.rate, GST_SECOND);
    GST_DEBUG_OBJECT (sink, "clipping stop to %" GST_TIME_FORMAT " %"
        G_GUINT64_FORMAT " samples", GST_TIME_ARGS (cstop), diff);
    samples -= diff;
    stop = cstop;
  }

588
  gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
589
      &crate_num, &crate_denom);
Wim Taymans's avatar
Wim Taymans committed
590

591
592
593
  /* bring buffer timestamp to running time */
  render_time =
      gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time);
Wim Taymans's avatar
Wim Taymans committed
594
  /* add base time to get absolute clock time */
Wim Taymans's avatar
Wim Taymans committed
595
596
597
  render_time +=
      (gst_element_get_base_time (GST_ELEMENT_CAST (bsink)) - cexternal) +
      cinternal;
Wim Taymans's avatar
Wim Taymans committed
598
  /* and bring the time to the offset in the buffer */
599
600
601
602
603
604
  render_offset =
      gst_util_uint64_scale_int (render_time, ringbuf->spec.rate, GST_SECOND);

  GST_DEBUG_OBJECT (sink, "render time %" GST_TIME_FORMAT
      ", render offset %llu, samples %lu",
      GST_TIME_ARGS (render_time), render_offset, samples);
Wim Taymans's avatar
Wim Taymans committed
605

606
607
608
609
610
611
612
613
614
615
616
617

  /* never try to align samples when we are slaved to another clock, just
   * trust the rate control algorithm to align the two clocks. We don't take
   * the LOCK to read the clock because it does not really matter here and the
   * clock is not changed while playing normally. */
  if (GST_ELEMENT_CLOCK (sink) != sink->provided_clock) {
    GST_DEBUG_OBJECT (sink, "no align needed: we are slaved");
    goto no_align;
  }

  /* always resync after a discont */
  if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
618
    GST_DEBUG_OBJECT (sink, "resync after discont");
619
620
621
    goto no_align;
  }

622
623
624
625
626
627
  if (G_UNLIKELY (sink->next_sample == -1)) {
    GST_DEBUG_OBJECT (sink,
        "no align possible: no previous sample position known");
    goto no_align;
  }

628
629
630
631
632
633
634
  /* now try to align the sample to the previous one */
  diff = ABS ((gint64) render_offset - (gint64) sink->next_sample);

  /* we tollerate half a second diff before we start resyncing. This
   * should be enough to compensate for various rounding errors in the timestamp
   * and sample offset position. We always resync if we got a discont anyway and
   * non-discont should be aligned by definition. */
635
  if (G_LIKELY (diff < ringbuf->spec.rate / DIFF_TOLERANCE)) {
636
637
638
639
640
641
642
643
    GST_DEBUG_OBJECT (sink,
        "align with prev sample, %" G_GINT64_FORMAT " < %lu", diff,
        ringbuf->spec.rate / DIFF_TOLERANCE);
    /* just align with previous sample then */
    render_offset = sink->next_sample;
  } else {
    /* timestamps drifted apart from previous samples too much, we need to
     * resync. We log this as an element warning. */
Wim Taymans's avatar
Wim Taymans committed
644
645
646
647
648
    GST_ELEMENT_WARNING (sink, CORE, CLOCK,
        ("Compensating for audio synchronisation problems"),
        ("Unexpected discontinuity in audio timestamps of more "
            "than half a second (%" GST_TIME_FORMAT "), resyncing",
            GST_TIME_ARGS (diff)));
649
  }
Wim Taymans's avatar
Wim Taymans committed
650

651
no_align:
652
653
  crate =
      gst_guint64_to_gdouble (crate_num) / gst_guint64_to_gdouble (crate_denom);
654
655
656
  GST_DEBUG_OBJECT (sink,
      "internal %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", rate %g",
      cinternal, cexternal, crate);
657

658
no_sync:
Wim Taymans's avatar
Wim Taymans committed
659
  /* clip length based on rate */
660
  samples = MIN (samples, samples / (crate * bsink->segment.abs_rate));
661
662

  /* the next sample should be current sample and its length */
Wim Taymans's avatar
Wim Taymans committed
663
664
  sink->next_sample = render_offset + samples;

665
666
667
668
669
670
671
  do {
    written = gst_ring_buffer_commit (ringbuf, render_offset, data, samples);
    GST_DEBUG_OBJECT (sink, "wrote %u of %u", written, samples);
    /* if we wrote all, we're done */
    if (written == samples)
      break;

672
673
    /* else something interrupted us and we wait for preroll. */
    if (gst_base_sink_wait_preroll (bsink) != GST_FLOW_OK)
674
675
676
677
678
679
      goto stopping;

    render_offset += written;
    samples -= written;
    data += written * bps;
  } while (TRUE);
Wim Taymans's avatar
Wim Taymans committed
680

681
682
683
  if (GST_CLOCK_TIME_IS_VALID (stop) && stop >= bsink->segment.stop) {
    GST_DEBUG_OBJECT (sink,
        "start playback because we are at the end of segment");
Wim Taymans's avatar
Wim Taymans committed
684
685
    gst_ring_buffer_start (ringbuf);
  }
686
687

  return GST_FLOW_OK;
688

689
  /* SPECIAL cases */
Wim Taymans's avatar
Wim Taymans committed
690
691
out_of_segment:
  {
692
693
694
695
    GST_DEBUG_OBJECT (sink,
        "dropping sample out of segment time %" GST_TIME_FORMAT ", start %"
        GST_TIME_FORMAT, GST_TIME_ARGS (time),
        GST_TIME_ARGS (bsink->segment.start));
Wim Taymans's avatar
Wim Taymans committed
696
697
    return GST_FLOW_OK;
  }
698
  /* ERRORS */
699
700
wrong_state:
  {
701
    GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated");
702
    GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated."));
703
    return GST_FLOW_NOT_NEGOTIATED;
704
  }
Wim Taymans's avatar
Wim Taymans committed
705
706
wrong_size:
  {
707
    GST_DEBUG_OBJECT (sink, "wrong size");
708
709
    GST_ELEMENT_ERROR (sink, STREAM, WRONG_TYPE,
        (NULL), ("sink received buffer of wrong size."));
Wim Taymans's avatar
Wim Taymans committed
710
711
    return GST_FLOW_ERROR;
  }
712
713
714
715
716
stopping:
  {
    GST_DEBUG_OBJECT (sink, "ringbuffer is stopping");
    return GST_FLOW_WRONG_STATE;
  }
717
718
}

Wim Taymans's avatar
Wim Taymans committed
719
720
721
722
723
724
725
726
727
728
/**
 * gst_base_audio_sink_create_ringbuffer:
 * @sink: a #GstBaseAudioSink.
 *
 * Create and return the #GstRingBuffer for @sink. This function will call the
 * ::create_ringbuffer vmethod and will set @sink as the parent of the returned
 * buffer (see gst_object_set_parent()).
 *
 * Returns: The new ringbuffer of @sink.
 */
729
GstRingBuffer *
730
gst_base_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
731
732
733
734
{
  GstBaseAudioSinkClass *bclass;
  GstRingBuffer *buffer = NULL;

735
  bclass = GST_BASE_AUDIO_SINK_GET_CLASS (sink);
736
737
738
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (sink);

739
  if (buffer)
740
741
742
743
744
    gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (sink));

  return buffer;
}

745
static void
746
gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
747
    gpointer user_data)
748
{
749
  /* GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (data); */
750
751
}

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
/* should be called with the LOCK */
static GstStateChangeReturn
gst_base_audio_sink_async_play (GstBaseSink * basesink)
{
  GstClock *clock;
  GstClockTime time, base;
  GstBaseAudioSink *sink;

  sink = GST_BASE_AUDIO_SINK (basesink);

  GST_DEBUG_OBJECT (sink, "ringbuffer may start now");
  gst_ring_buffer_may_start (sink->ringbuffer, TRUE);

  clock = GST_ELEMENT_CLOCK (sink);
  if (clock == NULL)
    goto no_clock;

  /* FIXME, only start slaving when we really start the ringbuffer */
  /* if we are slaved to a clock, we need to set the initial
   * calibration */
  if (clock != sink->provided_clock) {
    GstClockTime rate_num, rate_denom;

    base = GST_ELEMENT_CAST (sink)->base_time;
    time = gst_clock_get_internal_time (sink->provided_clock);

    GST_DEBUG_OBJECT (sink,
        "time: %" GST_TIME_FORMAT " base: %" GST_TIME_FORMAT,
        GST_TIME_ARGS (time), GST_TIME_ARGS (base));

    /* FIXME, this is not yet accurate enough for smooth playback */
    gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num,
        &rate_denom);
    /* Does not work yet. */
    gst_clock_set_calibration (sink->provided_clock, time, base,
        rate_num, rate_denom);

    gst_clock_set_master (sink->provided_clock, clock);
  }

no_clock:
  return GST_STATE_CHANGE_SUCCESS;
}

static GstStateChangeReturn
gst_base_audio_sink_do_play (GstBaseAudioSink * sink)
{
  GstStateChangeReturn ret;

  GST_OBJECT_LOCK (sink);
  ret = gst_base_audio_sink_async_play (GST_BASE_SINK_CAST (sink));
  GST_OBJECT_UNLOCK (sink);

  return ret;
}

808
809
810
static GstStateChangeReturn
gst_base_audio_sink_change_state (GstElement * element,
    GstStateChange transition)
811
{
812
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
813
  GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (element);
814
815

  switch (transition) {
816
    case GST_STATE_CHANGE_NULL_TO_READY:
817
818
819
820
821
      if (sink->ringbuffer == NULL) {
        sink->ringbuffer = gst_base_audio_sink_create_ringbuffer (sink);
        gst_ring_buffer_set_callback (sink->ringbuffer,
            gst_base_audio_sink_callback, sink);
      }
822
      if (!gst_ring_buffer_open_device (sink->ringbuffer))
823
        goto open_failed;
824
      break;
825
    case GST_STATE_CHANGE_READY_TO_PAUSED:
826
      sink->next_sample = -1;
827
828
      gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
      gst_ring_buffer_may_start (sink->ringbuffer, FALSE);
829
      break;
830
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
831
      gst_base_audio_sink_do_play (sink);
Wim Taymans's avatar
Wim Taymans committed
832
      break;
833
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
834
835
836
      /* need to take the lock so we don't interfere with an
       * async play */
      GST_OBJECT_LOCK (sink);
837
838
      /* ringbuffer cannot start anymore */
      gst_ring_buffer_may_start (sink->ringbuffer, FALSE);
839
      gst_ring_buffer_pause (sink->ringbuffer);
840
      GST_OBJECT_UNLOCK (sink);
841
      break;
Wim Taymans's avatar
Wim Taymans committed
842
    case GST_STATE_CHANGE_PAUSED_TO_READY:
843
844
      /* make sure we unblock before calling the parent state change
       * so it can grab the STREAM_LOCK */
Wim Taymans's avatar
Wim Taymans committed
845
      gst_ring_buffer_set_flushing (sink->ringbuffer, TRUE);
846
847
848
849
850
      break;
    default:
      break;
  }

851
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
852
853

  switch (transition) {
854
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
855
856
      /* slop slaving ourselves to the master, if any */
      gst_clock_set_master (sink->provided_clock, NULL);
857
      break;
858
    case GST_STATE_CHANGE_PAUSED_TO_READY:
859
      gst_ring_buffer_release (sink->ringbuffer);
860
      gst_pad_set_caps (GST_BASE_SINK_PAD (sink), NULL);
861
      break;
862
    case GST_STATE_CHANGE_READY_TO_NULL:
863
      gst_ring_buffer_close_device (sink->ringbuffer);
864
865
866
867
868
869
      break;
    default:
      break;
  }

  return ret;
870

871
  /* ERRORS */
872
873
open_failed:
  {
874
    /* subclass must post a meaningfull error message */
875
876
877
    GST_DEBUG_OBJECT (sink, "open failed");
    return GST_STATE_CHANGE_FAILURE;
  }
878
}