gstbaseaudiosrc.c 34 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
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2005 Wim Taymans <wim@fluendo.com>
 *
 * gstbaseaudiosrc.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:gstbaseaudiosrc
 * @short_description: Base class for audio sources
 * @see_also: #GstAudioSrc, #GstRingBuffer.
 *
 * This is the base class for audio sources. Subclasses need to implement the
 * ::create_ringbuffer vmethod. This base class will then take care of
 * reading samples from the ringbuffer, synchronisation and flushing.
 *
 * Last reviewed on 2006-09-27 (0.10.12)
 */

35
36
37
38
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

Wim Taymans's avatar
Wim Taymans committed
39
40
41
42
#include <string.h>

#include "gstbaseaudiosrc.h"

43
44
#include "gst/gst-i18n-plugin.h"

45
46
GST_DEBUG_CATEGORY_STATIC (gst_base_audio_src_debug);
#define GST_CAT_DEFAULT gst_base_audio_src_debug
Wim Taymans's avatar
Wim Taymans committed
47

48
GType
49
gst_base_audio_src_slave_method_get_type (void)
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
{
  static GType slave_method_type = 0;
  static const GEnumValue slave_method[] = {
    {GST_BASE_AUDIO_SRC_SLAVE_RESAMPLE, "Resampling slaving", "resample"},
    {GST_BASE_AUDIO_SRC_SLAVE_RETIMESTAMP, "Re-timestamp", "re-timestamp"},
    {GST_BASE_AUDIO_SRC_SLAVE_SKEW, "Skew", "skew"},
    {GST_BASE_AUDIO_SRC_SLAVE_NONE, "No slaving", "none"},
    {0, NULL, NULL},
  };

  if (!slave_method_type) {
    slave_method_type =
        g_enum_register_static ("GstBaseAudioSrcSlaveMethod", slave_method);
  }
  return slave_method_type;
}

67
68
69
70
71
72
#define GST_BASE_AUDIO_SRC_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_AUDIO_SRC, GstBaseAudioSrcPrivate))

struct _GstBaseAudioSrcPrivate
{
  gboolean provide_clock;
73
74
75

  /* the clock slaving algorithm in use */
  GstBaseAudioSrcSlaveMethod slave_method;
76
77
};

Wim Taymans's avatar
Wim Taymans committed
78
79
80
81
82
83
84
/* BaseAudioSrc signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

85
86
#define DEFAULT_BUFFER_TIME     ((200 * GST_MSECOND) / GST_USECOND)
#define DEFAULT_LATENCY_TIME    ((10 * GST_MSECOND) / GST_USECOND)
87
88
#define DEFAULT_ACTUAL_BUFFER_TIME     -1
#define DEFAULT_ACTUAL_LATENCY_TIME    -1
89
#define DEFAULT_PROVIDE_CLOCK   TRUE
90
#define DEFAULT_SLAVE_METHOD    GST_BASE_AUDIO_SRC_SLAVE_RETIMESTAMP
91

Wim Taymans's avatar
Wim Taymans committed
92
93
94
95
96
enum
{
  PROP_0,
  PROP_BUFFER_TIME,
  PROP_LATENCY_TIME,
97
98
  PROP_ACTUAL_BUFFER_TIME,
  PROP_ACTUAL_LATENCY_TIME,
99
100
101
  PROP_PROVIDE_CLOCK,
  PROP_SLAVE_METHOD,
  PROP_LAST
Wim Taymans's avatar
Wim Taymans committed
102
103
};

104
105
106
107
108
109
110
111
112
113
static void
_do_init (GType type)
{
  GST_DEBUG_CATEGORY_INIT (gst_base_audio_src_debug, "baseaudiosrc", 0,
      "baseaudiosrc element");

#ifdef ENABLE_NLS
  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
      LOCALEDIR);
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
114
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
115
116
#endif /* ENABLE_NLS */
}
Wim Taymans's avatar
Wim Taymans committed
117

118
GST_BOILERPLATE_FULL (GstBaseAudioSrc, gst_base_audio_src, GstPushSrc,
119
    GST_TYPE_PUSH_SRC, _do_init);
Wim Taymans's avatar
Wim Taymans committed
120

121
static void gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
122
    const GValue * value, GParamSpec * pspec);
123
static void gst_base_audio_src_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
124
    GValue * value, GParamSpec * pspec);
125
static void gst_base_audio_src_dispose (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
126

127
128
static GstStateChangeReturn gst_base_audio_src_change_state (GstElement *
    element, GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
129

130
static GstClock *gst_base_audio_src_provide_clock (GstElement * elem);
131
static GstClockTime gst_base_audio_src_get_time (GstClock * clock,
Wim Taymans's avatar
Wim Taymans committed
132
133
    GstBaseAudioSrc * src);

134
135
136
static GstFlowReturn gst_base_audio_src_create (GstBaseSrc * bsrc,
    guint64 offset, guint length, GstBuffer ** buf);
static gboolean gst_base_audio_src_check_get_range (GstBaseSrc * bsrc);
Wim Taymans's avatar
Wim Taymans committed
137

138
139
static gboolean gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event);
static void gst_base_audio_src_get_times (GstBaseSrc * bsrc,
Wim Taymans's avatar
Wim Taymans committed
140
    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
141
static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
142
static gboolean gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query);
143
static void gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
144

145
/* static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 }; */
Wim Taymans's avatar
Wim Taymans committed
146
147

static void
148
gst_base_audio_src_base_init (gpointer g_class)
Wim Taymans's avatar
Wim Taymans committed
149
150
151
152
{
}

static void
153
gst_base_audio_src_class_init (GstBaseAudioSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
154
155
156
157
158
159
160
161
162
163
164
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseSrcClass *gstbasesrc_class;
  GstPushSrcClass *gstpushsrc_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbasesrc_class = (GstBaseSrcClass *) klass;
  gstpushsrc_class = (GstPushSrcClass *) klass;

165
166
  g_type_class_add_private (klass, sizeof (GstBaseAudioSrcPrivate));

Wim Taymans's avatar
Wim Taymans committed
167
  gobject_class->set_property =
168
      GST_DEBUG_FUNCPTR (gst_base_audio_src_set_property);
Wim Taymans's avatar
Wim Taymans committed
169
  gobject_class->get_property =
170
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_property);
171
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_base_audio_src_dispose);
Wim Taymans's avatar
Wim Taymans committed
172

173
174
175
  g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
      g_param_spec_int64 ("buffer-time", "Buffer Time",
          "Size of audio buffer in microseconds", 1,
176
177
          G_MAXINT64, DEFAULT_BUFFER_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
178
179
180
181

  g_object_class_install_property (gobject_class, PROP_LATENCY_TIME,
      g_param_spec_int64 ("latency-time", "Latency Time",
          "Audio latency in microseconds", 1,
182
183
          G_MAXINT64, DEFAULT_LATENCY_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
184

185
186
187
188
189
190
191
  /**
   * GstBaseAudioSrc:actual-buffer-time:
   *
   * Actual configured size of audio buffer in microseconds.
   *
   * Since: 0.10.20
   **/
192
193
194
195
196
197
  g_object_class_install_property (gobject_class, PROP_ACTUAL_BUFFER_TIME,
      g_param_spec_int64 ("actual-buffer-time", "Actual Buffer Time",
          "Actual configured size of audio buffer in microseconds",
          DEFAULT_ACTUAL_BUFFER_TIME, G_MAXINT64, DEFAULT_ACTUAL_BUFFER_TIME,
          G_PARAM_READABLE));

198
199
200
201
202
203
204
  /**
   * GstBaseAudioSrc:actual-latency-time:
   *
   * Actual configured audio latency in microseconds.
   *
   * Since: 0.10.20
   **/
205
206
207
208
209
210
  g_object_class_install_property (gobject_class, PROP_ACTUAL_LATENCY_TIME,
      g_param_spec_int64 ("actual-latency-time", "Actual Latency Time",
          "Actual configured audio latency in microseconds",
          DEFAULT_ACTUAL_LATENCY_TIME, G_MAXINT64, DEFAULT_ACTUAL_LATENCY_TIME,
          G_PARAM_READABLE));

211
212
213
  g_object_class_install_property (gobject_class, PROP_PROVIDE_CLOCK,
      g_param_spec_boolean ("provide-clock", "Provide Clock",
          "Provide a clock to be used as the global pipeline clock",
214
          DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215

216
217
218
  g_object_class_install_property (gobject_class, PROP_SLAVE_METHOD,
      g_param_spec_enum ("slave-method", "Slave Method",
          "Algorithm to use to match the rate of the masterclock",
219
          GST_TYPE_BASE_AUDIO_SRC_SLAVE_METHOD, DEFAULT_SLAVE_METHOD,
220
221
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

Wim Taymans's avatar
Wim Taymans committed
222
  gstelement_class->change_state =
223
      GST_DEBUG_FUNCPTR (gst_base_audio_src_change_state);
224
225
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_provide_clock);
Wim Taymans's avatar
Wim Taymans committed
226

227
228
  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_src_setcaps);
  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_src_event);
229
  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_base_audio_src_query);
230
231
  gstbasesrc_class->get_times =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_times);
232
233
234
  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_base_audio_src_create);
  gstbasesrc_class->check_get_range =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_check_get_range);
235
  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_base_audio_src_fixate);
236
237
238
239

  /* ref class from a thread-safe context to work around missing bit of
   * thread-safety in GObject */
  g_type_class_ref (GST_TYPE_AUDIO_CLOCK);
240
  g_type_class_ref (GST_TYPE_RING_BUFFER);
Wim Taymans's avatar
Wim Taymans committed
241
242
243
}

static void
244
245
gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
    GstBaseAudioSrcClass * g_class)
Wim Taymans's avatar
Wim Taymans committed
246
{
247
248
  baseaudiosrc->priv = GST_BASE_AUDIO_SRC_GET_PRIVATE (baseaudiosrc);

Wim Taymans's avatar
Wim Taymans committed
249
250
  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;
251
  baseaudiosrc->priv->provide_clock = DEFAULT_PROVIDE_CLOCK;
252
  baseaudiosrc->priv->slave_method = DEFAULT_SLAVE_METHOD;
253
254
255
  /* reset blocksize we use latency time to calculate a more useful 
   * value based on negotiated format. */
  GST_BASE_SRC (baseaudiosrc)->blocksize = 0;
Wim Taymans's avatar
Wim Taymans committed
256

257
  baseaudiosrc->clock = gst_audio_clock_new ("GstAudioSrcClock",
258
      (GstAudioClockGetTimeFunc) gst_base_audio_src_get_time, baseaudiosrc);
Wim Taymans's avatar
Wim Taymans committed
259

260
  /* we are always a live source */
261
  gst_base_src_set_live (GST_BASE_SRC (baseaudiosrc), TRUE);
Wim Taymans's avatar
Wim Taymans committed
262
  /* we operate in time */
263
  gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME);
Wim Taymans's avatar
Wim Taymans committed
264
265
}

266
267
268
269
270
271
272
static void
gst_base_audio_src_dispose (GObject * object)
{
  GstBaseAudioSrc *src;

  src = GST_BASE_AUDIO_SRC (object);

273
  GST_OBJECT_LOCK (src);
274
275
276
277
278
279
280
281
  if (src->clock)
    gst_object_unref (src->clock);
  src->clock = NULL;

  if (src->ringbuffer) {
    gst_object_unparent (GST_OBJECT_CAST (src->ringbuffer));
    src->ringbuffer = NULL;
  }
282
  GST_OBJECT_UNLOCK (src);
283
284
285
286

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

Wim Taymans's avatar
Wim Taymans committed
287
static GstClock *
288
gst_base_audio_src_provide_clock (GstElement * elem)
Wim Taymans's avatar
Wim Taymans committed
289
290
{
  GstBaseAudioSrc *src;
291
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
292

293
  src = GST_BASE_AUDIO_SRC (elem);
Wim Taymans's avatar
Wim Taymans committed
294

295
296
297
298
299
300
301
  /* we have no ringbuffer (must be NULL state) */
  if (src->ringbuffer == NULL)
    goto wrong_state;

  if (!gst_ring_buffer_is_acquired (src->ringbuffer))
    goto wrong_state;

302
303
304
305
  GST_OBJECT_LOCK (src);
  if (!src->priv->provide_clock)
    goto clock_disabled;

306
  clock = GST_CLOCK_CAST (gst_object_ref (src->clock));
307
  GST_OBJECT_UNLOCK (src);
308
309
310
311
312
313
314
315
316

  return clock;

  /* ERRORS */
wrong_state:
  {
    GST_DEBUG_OBJECT (src, "ringbuffer not acquired");
    return NULL;
  }
317
318
319
320
321
322
clock_disabled:
  {
    GST_DEBUG_OBJECT (src, "clock provide disabled");
    GST_OBJECT_UNLOCK (src);
    return NULL;
  }
Wim Taymans's avatar
Wim Taymans committed
323
324
325
}

static GstClockTime
326
gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
327
{
328
329
  guint64 raw, samples;
  guint delay;
Wim Taymans's avatar
Wim Taymans committed
330
331
  GstClockTime result;

332
333
  if (G_UNLIKELY (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0))
    return GST_CLOCK_TIME_NONE;
Wim Taymans's avatar
Wim Taymans committed
334

335
336
337
338
339
340
341
  raw = samples = gst_ring_buffer_samples_done (src->ringbuffer);

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

  samples += delay;
Wim Taymans's avatar
Wim Taymans committed
342

343
344
  result = gst_util_uint64_scale_int (samples, GST_SECOND,
      src->ringbuffer->spec.rate);
Wim Taymans's avatar
Wim Taymans committed
345

346
347
348
349
  GST_DEBUG_OBJECT (src,
      "processed samples: raw %llu, delay %u, real %llu, time %"
      GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));

Wim Taymans's avatar
Wim Taymans committed
350
351
352
  return result;
}

353
354
355
356
357
358
359
360
361
362
static gboolean
gst_base_audio_src_check_get_range (GstBaseSrc * bsrc)
{
  /* we allow limited pull base operation of which the details
   * will eventually exposed in an as of yet non-existing query.
   * Basically pulling can be done on any number of bytes as long
   * as the offset is -1 or sequentially increasing. */
  return TRUE;
}

363
364
365
366
367
368
369
370
371
372
373
374
375
376
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
/**
 * gst_base_audio_src_set_provide_clock:
 * @src: a #GstBaseAudioSrc
 * @provide: new state
 *
 * Controls whether @src will provide a clock or not. If @provide is %TRUE, 
 * gst_element_provide_clock() will return a clock that reflects the datarate
 * of @src. If @provide is %FALSE, gst_element_provide_clock() will return NULL.
 *
 * Since: 0.10.16
 */
void
gst_base_audio_src_set_provide_clock (GstBaseAudioSrc * src, gboolean provide)
{
  g_return_if_fail (GST_IS_BASE_AUDIO_SRC (src));

  GST_OBJECT_LOCK (src);
  src->priv->provide_clock = provide;
  GST_OBJECT_UNLOCK (src);
}

/**
 * gst_base_audio_src_get_provide_clock:
 * @src: a #GstBaseAudioSrc
 *
 * Queries whether @src will provide a clock or not. See also
 * gst_base_audio_src_set_provide_clock.
 *
 * Returns: %TRUE if @src will provide a clock.
 *
 * Since: 0.10.16
 */
gboolean
gst_base_audio_src_get_provide_clock (GstBaseAudioSrc * src)
{
  gboolean result;

  g_return_val_if_fail (GST_IS_BASE_AUDIO_SRC (src), FALSE);

  GST_OBJECT_LOCK (src);
  result = src->priv->provide_clock;
  GST_OBJECT_UNLOCK (src);

  return result;
}

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/**
 * gst_base_audio_src_set_slave_method:
 * @src: a #GstBaseAudioSrc
 * @method: the new slave method
 *
 * Controls how clock slaving will be performed in @src. 
 *
 * Since: 0.10.20
 */
void
gst_base_audio_src_set_slave_method (GstBaseAudioSrc * src,
    GstBaseAudioSrcSlaveMethod method)
{
  g_return_if_fail (GST_IS_BASE_AUDIO_SRC (src));

  GST_OBJECT_LOCK (src);
  src->priv->slave_method = method;
  GST_OBJECT_UNLOCK (src);
}

/**
 * gst_base_audio_src_get_slave_method:
 * @src: a #GstBaseAudioSrc
 *
 * Get the current slave method used by @src.
 *
 * Returns: The current slave method used by @src.
 *
 * Since: 0.10.20
 */
GstBaseAudioSrcSlaveMethod
gst_base_audio_src_get_slave_method (GstBaseAudioSrc * src)
{
  GstBaseAudioSrcSlaveMethod result;

  g_return_val_if_fail (GST_IS_BASE_AUDIO_SRC (src), -1);

  GST_OBJECT_LOCK (src);
  result = src->priv->slave_method;
  GST_OBJECT_UNLOCK (src);

  return result;
}

Wim Taymans's avatar
Wim Taymans committed
453
static void
454
gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
455
456
457
458
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSrc *src;

459
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
460
461
462
463
464
465
466
467

  switch (prop_id) {
    case PROP_BUFFER_TIME:
      src->buffer_time = g_value_get_int64 (value);
      break;
    case PROP_LATENCY_TIME:
      src->latency_time = g_value_get_int64 (value);
      break;
468
    case PROP_PROVIDE_CLOCK:
469
      gst_base_audio_src_set_provide_clock (src, g_value_get_boolean (value));
470
      break;
471
472
473
    case PROP_SLAVE_METHOD:
      gst_base_audio_src_set_slave_method (src, g_value_get_enum (value));
      break;
Wim Taymans's avatar
Wim Taymans committed
474
475
476
477
478
479
480
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
481
482
gst_base_audio_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
483
484
485
{
  GstBaseAudioSrc *src;

486
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
487
488
489
490
491
492
493
494

  switch (prop_id) {
    case PROP_BUFFER_TIME:
      g_value_set_int64 (value, src->buffer_time);
      break;
    case PROP_LATENCY_TIME:
      g_value_set_int64 (value, src->latency_time);
      break;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
    case PROP_ACTUAL_BUFFER_TIME:
      GST_OBJECT_LOCK (src);
      if (src->ringbuffer && src->ringbuffer->acquired)
        g_value_set_int64 (value, src->ringbuffer->spec.buffer_time);
      else
        g_value_set_int64 (value, DEFAULT_ACTUAL_BUFFER_TIME);
      GST_OBJECT_UNLOCK (src);
      break;
    case PROP_ACTUAL_LATENCY_TIME:
      GST_OBJECT_LOCK (src);
      if (src->ringbuffer && src->ringbuffer->acquired)
        g_value_set_int64 (value, src->ringbuffer->spec.latency_time);
      else
        g_value_set_int64 (value, DEFAULT_ACTUAL_LATENCY_TIME);
      GST_OBJECT_UNLOCK (src);
      break;
511
    case PROP_PROVIDE_CLOCK:
512
      g_value_set_boolean (value, gst_base_audio_src_get_provide_clock (src));
513
      break;
514
515
516
    case PROP_SLAVE_METHOD:
      g_value_set_enum (value, gst_base_audio_src_get_slave_method (src));
      break;
Wim Taymans's avatar
Wim Taymans committed
517
518
519
520
521
522
523
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
524
gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
525
526
{
  GstStructure *s;
527
  gint width, depth;
Wim Taymans's avatar
Wim Taymans committed
528
529
530

  s = gst_caps_get_structure (caps, 0);

531
  /* fields for all formats */
532
533
534
  gst_structure_fixate_field_nearest_int (s, "rate", 44100);
  gst_structure_fixate_field_nearest_int (s, "channels", 2);
  gst_structure_fixate_field_nearest_int (s, "width", 16);
535
536
537
538
539
540
541
542
543
544

  /* fields for int */
  if (gst_structure_has_field (s, "depth")) {
    gst_structure_get_int (s, "width", &width);
    /* round width to nearest multiple of 8 for the depth */
    depth = GST_ROUND_UP_8 (width);
    gst_structure_fixate_field_nearest_int (s, "depth", depth);
  }
  if (gst_structure_has_field (s, "signed"))
    gst_structure_fixate_field_boolean (s, "signed", TRUE);
545
  if (gst_structure_has_field (s, "endianness"))
546
    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
Wim Taymans's avatar
Wim Taymans committed
547
548
549
}

static gboolean
550
gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
551
{
552
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
553
554
555
556
557
558
559
  GstRingBufferSpec *spec;

  spec = &src->ringbuffer->spec;

  spec->buffer_time = src->buffer_time;
  spec->latency_time = src->latency_time;

560
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
561
562
563
564
565
566
567
568
569
    goto parse_error;

  /* calculate suggested segsize and segtotal */
  spec->segsize =
      spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
  spec->segtotal = spec->buffer_time / spec->latency_time;

  GST_DEBUG ("release old ringbuffer");

570
  gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
571

572
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
573
574
575

  GST_DEBUG ("acquire new ringbuffer");

576
  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
Wim Taymans's avatar
Wim Taymans committed
577
578
579
580
581
582
583
584
585
    goto acquire_error;

  /* calculate actual latency and buffer times */
  spec->latency_time =
      spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample);
  spec->buffer_time =
      spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate *
      spec->bytes_per_sample);

586
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
587

588
589
590
  g_object_notify (G_OBJECT (src), "actual-buffer-time");
  g_object_notify (G_OBJECT (src), "actual-latency-time");

Wim Taymans's avatar
Wim Taymans committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  return TRUE;

  /* ERRORS */
parse_error:
  {
    GST_DEBUG ("could not parse caps");
    return FALSE;
  }
acquire_error:
  {
    GST_DEBUG ("could not acquire ringbuffer");
    return FALSE;
  }
}

static void
607
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
608
609
    GstClockTime * start, GstClockTime * end)
{
610
611
  /* no need to sync to a clock here, we schedule the samples based
   * on our own clock for the moment. */
Wim Taymans's avatar
Wim Taymans committed
612
613
614
615
  *start = GST_CLOCK_TIME_NONE;
  *end = GST_CLOCK_TIME_NONE;
}

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
static gboolean
gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query)
{
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
  gboolean res = FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_LATENCY:
    {
      GstClockTime min_latency, max_latency;
      GstRingBufferSpec *spec;

      if (G_UNLIKELY (src->ringbuffer == NULL
              || src->ringbuffer->spec.rate == 0))
        goto done;

      spec = &src->ringbuffer->spec;

634
      /* we have at least 1 segment of latency */
635
636
637
      min_latency =
          gst_util_uint64_scale_int (spec->segsize, GST_SECOND,
          spec->rate * spec->bytes_per_sample);
638
      /* we cannot delay more than the buffersize else we lose data */
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
      max_latency =
          gst_util_uint64_scale_int (spec->segtotal * spec->segsize, GST_SECOND,
          spec->rate * spec->bytes_per_sample);

      GST_DEBUG_OBJECT (src,
          "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
          GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));

      /* we are always live, the min latency is 1 segment and the max latency is
       * the complete buffer of segments. */
      gst_query_set_latency (query, TRUE, min_latency, max_latency);

      res = TRUE;
      break;
    }
    default:
      res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
      break;
  }
done:
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
662
static gboolean
663
gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
664
{
665
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
666
667

  switch (GST_EVENT_TYPE (event)) {
668
669
    case GST_EVENT_FLUSH_START:
      gst_ring_buffer_pause (src->ringbuffer);
670
      gst_ring_buffer_clear_all (src->ringbuffer);
671
672
      break;
    case GST_EVENT_FLUSH_STOP:
673
674
675
      /* always resync on sample after a flush */
      src->next_sample = -1;
      gst_ring_buffer_clear_all (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
676
677
678
679
680
681
682
      break;
    default:
      break;
  }
  return TRUE;
}

683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
/* get the next offset in the ringbuffer for reading samples.
 * If the next sample is too far away, this function will position itself to the
 * next most recent sample, creating discontinuity */
static guint64
gst_base_audio_src_get_offset (GstBaseAudioSrc * src)
{
  guint64 sample;
  gint readseg, segdone, segtotal, sps;
  gint diff;

  /* assume we can append to the previous sample */
  sample = src->next_sample;
  /* no previous sample, try to read from position 0 */
  if (sample == -1)
    sample = 0;

  sps = src->ringbuffer->samples_per_seg;
  segtotal = src->ringbuffer->spec.segtotal;

  /* figure out the segment and the offset inside the segment where
   * the sample should be read from. */
  readseg = sample / sps;

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

710
711
  GST_DEBUG_OBJECT (src, "reading from %d, we are at %d", readseg, segdone);

712
713
714
715
716
  /* see how far away it is from the read segment, normally segdone (where new
   * data is written in the ringbuffer) is bigger than readseg (where we are
   * reading). */
  diff = segdone - readseg;
  if (diff >= segtotal) {
717
    GST_DEBUG_OBJECT (src, "dropped, align to segment %d", segdone);
718
    /* sample would be dropped, position to next playable position */
719
    sample = ((guint64) (segdone - segtotal + 1)) * sps;
720
721
722
723
724
  }

  return sample;
}

Wim Taymans's avatar
Wim Taymans committed
725
static GstFlowReturn
726
727
gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
    GstBuffer ** outbuf)
Wim Taymans's avatar
Wim Taymans committed
728
{
729
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
730
731
  GstBuffer *buf;
  guchar *data;
732
  guint samples, total_samples;
733
  guint64 sample;
734
  gint bps;
735
  GstRingBuffer *ringbuffer;
736
  GstRingBufferSpec *spec;
737
  guint read;
738
  GstClockTime timestamp, duration;
739
  GstClock *clock;
740
741

  ringbuffer = src->ringbuffer;
742
  spec = &ringbuffer->spec;
Wim Taymans's avatar
Wim Taymans committed
743

744
  if (G_UNLIKELY (!gst_ring_buffer_is_acquired (ringbuffer)))
Wim Taymans's avatar
Wim Taymans committed
745
746
    goto wrong_state;

747
  bps = spec->bytes_per_sample;
Wim Taymans's avatar
Wim Taymans committed
748

749
750
  if ((length == 0 && bsrc->blocksize == 0) || length == -1)
    /* no length given, use the default segment size */
751
    length = spec->segsize;
752
753
754
  else
    /* make sure we round down to an integral number of samples */
    length -= length % bps;
Wim Taymans's avatar
Wim Taymans committed
755

756
  /* figure out the offset in the ringbuffer */
757
  if (G_UNLIKELY (offset != -1)) {
758
759
760
761
    sample = offset / bps;
    /* if a specific offset was given it must be the next sequential
     * offset we expect or we fail for now. */
    if (src->next_sample != -1 && sample != src->next_sample)
762
      goto wrong_offset;
763
764
765
766
  } else {
    /* calculate the sequentially next sample we need to read. This can jump and
     * create a DISCONT. */
    sample = gst_base_audio_src_get_offset (src);
767
768
  }

Wim Taymans's avatar
Wim Taymans committed
769
770
  GST_DEBUG_OBJECT (src, "reading from sample %" G_GUINT64_FORMAT, sample);

771
  /* get the number of samples to read */
772
  total_samples = samples = length / bps;
773
774
775
776

  /* FIXME, using a bufferpool would be nice here */
  buf = gst_buffer_new_and_alloc (length);
  data = GST_BUFFER_DATA (buf);
Wim Taymans's avatar
Wim Taymans committed
777

778
779
780
781
782
783
784
785
  do {
    read = gst_ring_buffer_read (ringbuffer, sample, data, samples);
    GST_DEBUG_OBJECT (src, "read %u of %u", read, samples);
    /* if we read all, we're done */
    if (read == samples)
      break;

    /* else something interrupted us and we wait for playing again. */
786
    GST_DEBUG_OBJECT (src, "wait playing");
787
788
789
    if (gst_base_src_wait_playing (bsrc) != GST_FLOW_OK)
      goto stopped;

790
791
    GST_DEBUG_OBJECT (src, "continue playing");

792
793
794
795
796
    /* read next samples */
    sample += read;
    samples -= read;
    data += read * bps;
  } while (TRUE);
Wim Taymans's avatar
Wim Taymans committed
797

798
799
800
801
802
  /* mark discontinuity if needed */
  if (G_UNLIKELY (sample != src->next_sample) && src->next_sample != -1) {
    GST_WARNING_OBJECT (src,
        "create DISCONT of %" G_GUINT64_FORMAT " samples at sample %"
        G_GUINT64_FORMAT, sample - src->next_sample, sample);
803
804
    GST_ELEMENT_WARNING (src, CORE, CLOCK,
        (_("Can't record audio fast enough")),
805
806
807
        ("Dropped %" G_GUINT64_FORMAT " samples. This is most likely because "
            "downstream can't keep up and is consuming samples too slowly.",
            sample - src->next_sample));
808
809
810
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
  }

811
812
813
814
815
816
817
  src->next_sample = sample + samples;

  /* get the normal timestamp to get the duration. */
  timestamp = gst_util_uint64_scale_int (sample, GST_SECOND, spec->rate);
  duration = gst_util_uint64_scale_int (src->next_sample, GST_SECOND,
      spec->rate) - timestamp;

818
  GST_OBJECT_LOCK (src);
819
820
821
822
823
  if (!(clock = GST_ELEMENT_CLOCK (src)))
    goto no_sync;

  if (clock != src->clock) {
    /* we are slaved, check how to handle this */
824
825
    switch (src->priv->slave_method) {
      case GST_BASE_AUDIO_SRC_SLAVE_RESAMPLE:
826
        /* not implemented, use skew algorithm. This algorithm should
827
828
829
         * work on the readout pointer and produces more or less samples based
         * on the clock drift */
      case GST_BASE_AUDIO_SRC_SLAVE_SKEW:
830
831
832
833
834
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
      {
        GstClockTime running_time;
        GstClockTime base_time;
        GstClockTime current_time;
        guint64 running_time_sample;
        gint running_time_segment;
        gint current_segment;
        gint segment_skew;
        gint sps;

        /* samples per segment */
        sps = ringbuffer->samples_per_seg;

        /* get the current time */
        current_time = gst_clock_get_time (clock);

        /* get the basetime */
        base_time = GST_ELEMENT_CAST (src)->base_time;

        /* get the running_time */
        running_time = current_time - base_time;

        /* the running_time converted to a sample (relative to the ringbuffer) */
        running_time_sample =
            gst_util_uint64_scale_int (running_time, spec->rate, GST_SECOND);

        /* the segmentnr corrensponding to running_time, round down */
        running_time_segment = running_time_sample / sps;

        /* the segment currently read from the ringbuffer */
        current_segment = sample / sps;

        /* the skew we have between running_time and the ringbuffertime */
        segment_skew = running_time_segment - current_segment;

865
866
        GST_DEBUG_OBJECT (bsrc, "\n running_time = %" GST_TIME_FORMAT
            "\n timestamp     = %" GST_TIME_FORMAT
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
            "\n running_time_segment = %d"
            "\n current_segment      = %d"
            "\n segment_skew         = %d",
            GST_TIME_ARGS (running_time),
            GST_TIME_ARGS (timestamp),
            running_time_segment, current_segment, segment_skew);

        /* Resync the ringbuffer if:
         * 1. We get one segment into the future.
         *    This is clearly a lie, because we can't
         *    possibly have a buffer with timestamp 1 at
         *    time 0. (unless it has time-travelled...)
         *
         * 2. We are more than the length of the ringbuffer behind.
         *    The length of the ringbuffer then gets to dictate
         *    the threshold for what is concidered "too late"
         *
         * 3. If this is our first buffer.
         *    We know that we should catch up to running_time
         *    the first time we are ran.
         */
        if ((segment_skew < 0) ||
            (segment_skew >= ringbuffer->spec.segtotal) ||
            (current_segment == 0)) {
          gint segments_written;
          gint first_segment;
          gint last_segment;
          gint new_last_segment;
          gint segment_diff;
          gint new_first_segment;
          guint64 new_sample;

          /* we are going to say that the last segment was captured at the current time
             (running_time), minus one segment of creation-latency in the ringbuffer.
             This can be thought of as: The segment arrived in the ringbuffer at time X, and
             that means it was created at time X - (one segment). */
          new_last_segment = running_time_segment - 1;

          /* for better readablity */
          first_segment = current_segment;

          /* get the amount of segments written from the device by now */
          segments_written = g_atomic_int_get (&ringbuffer->segdone);

          /* subtract the base to segments_written to get the number of the
             last written segment in the ringbuffer (one segment written = segment 0) */
          last_segment = segments_written - ringbuffer->segbase - 1;

          /* we see how many segments the ringbuffer was timeshifted */
          segment_diff = new_last_segment - last_segment;

          /* we move the first segment an equal amount */
          new_first_segment = first_segment + segment_diff;

          /* and we also move the segmentbase the same amount */
          ringbuffer->segbase -= segment_diff;

          /* we calculate the new sample value */
925
          new_sample = ((guint64) new_first_segment) * sps;
926
927
928
929
930
931
932
933
934
935

          /* and get the relative time to this -> our new timestamp */
          timestamp =
              gst_util_uint64_scale_int (new_sample, GST_SECOND, spec->rate);

          /* we update the next sample accordingly */
          src->next_sample = new_sample + samples;

          GST_DEBUG_OBJECT (bsrc,
              "Timeshifted the ringbuffer with %d segments: "
936
937
              "Updating the timestamp to %" GST_TIME_FORMAT ", "
              "and src->next_sample to %" G_GUINT64_FORMAT, segment_diff,
938
939
940
941
              GST_TIME_ARGS (timestamp), src->next_sample);
        }
        break;
      }
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
      case GST_BASE_AUDIO_SRC_SLAVE_RETIMESTAMP:
      {
        GstClockTime base_time, latency;

        /* We are slaved to another clock, take running time of the pipeline clock and
         * timestamp against it. Somebody else in the pipeline should figure out the
         * clock drift. We keep the duration we calculated above. */
        timestamp = gst_clock_get_time (clock);
        base_time = GST_ELEMENT_CAST (src)->base_time;

        if (timestamp > base_time)
          timestamp -= base_time;
        else
          timestamp = 0;

        /* subtract latency */
        latency =
            gst_util_uint64_scale_int (total_samples, GST_SECOND, spec->rate);
        if (timestamp > latency)
          timestamp -= latency;
        else
          timestamp = 0;
      }
      case GST_BASE_AUDIO_SRC_SLAVE_NONE:
        break;
    }
968
969
970
  } else {
    GstClockTime base_time;

971
972
973
    /* to get the timestamp against the clock we also need to add our offset */
    timestamp = gst_audio_clock_adjust (clock, timestamp);

974
975
976
977
978
979
980
    /* we are not slaved, subtract base_time */
    base_time = GST_ELEMENT_CAST (src)->base_time;

    if (timestamp > base_time)
      timestamp -= base_time;
    else
      timestamp = 0;
981
  }
982
983

no_sync:
984
  GST_OBJECT_UNLOCK (src);
Wim Taymans's avatar
Wim Taymans committed
985
986

  GST_BUFFER_TIMESTAMP (buf) = timestamp;
987
  GST_BUFFER_DURATION (buf) = duration;
988
989
  GST_BUFFER_OFFSET (buf) = sample;
  GST_BUFFER_OFFSET_END (buf) = sample + samples;
990

Wim Taymans's avatar
Wim Taymans committed
991
992
993
994
  *outbuf = buf;

  return GST_FLOW_OK;

995
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
996
997
wrong_state:
  {
998
    GST_DEBUG_OBJECT (src, "ringbuffer in wrong state");
Wim Taymans's avatar
Wim Taymans committed
999
1000
    return GST_FLOW_WRONG_STATE;
  }
1001
1002
1003
1004
1005
1006
1007
wrong_offset:
  {
    GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
        (NULL), ("resource can only be operated on sequentially but offset %"
            G_GUINT64_FORMAT " was given", offset));
    return GST_FLOW_ERROR;
  }
Wim Taymans's avatar
Wim Taymans committed
1008
1009
1010
stopped:
  {
    gst_buffer_unref (buf);
1011
    GST_DEBUG_OBJECT (src, "ringbuffer stopped");
Wim Taymans's avatar
Wim Taymans committed
1012
1013
1014
1015
    return GST_FLOW_WRONG_STATE;
  }
}

Wim Taymans's avatar
Wim Taymans committed
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
/**
 * gst_base_audio_src_create_ringbuffer:
 * @src: a #GstBaseAudioSrc.
 *
 * Create and return the #GstRingBuffer for @src. This function will call the
 * ::create_ringbuffer vmethod and will set @src as the parent of the returned
 * buffer (see gst_object_set_parent()).
 *
 * Returns: The new ringbuffer of @src.
 */
Wim Taymans's avatar
Wim Taymans committed
1026
GstRingBuffer *
1027
gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
1028
1029
1030
1031
{
  GstBaseAudioSrcClass *bclass;
  GstRingBuffer *buffer = NULL;

1032
  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
Wim Taymans's avatar
Wim Taymans committed
1033
1034
1035
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (src);

1036
1037
  if (G_LIKELY (buffer))
    gst_object_set_parent (GST_OBJECT_CAST (buffer), GST_OBJECT_CAST (src));
Wim Taymans's avatar
Wim Taymans committed
1038
1039
1040
1041

  return buffer;
}

1042
1043
1044
static GstStateChangeReturn
gst_base_audio_src_change_state (GstElement * element,
    GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
1045
{
1046
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1047
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
Wim Taymans's avatar
Wim Taymans committed
1048
1049

  switch (transition) {
1050
    case GST_STATE_CHANGE_NULL_TO_READY:
1051
      GST_DEBUG_OBJECT (src, "NULL->READY");
1052
      GST_OBJECT_LOCK (src);
1053
      if (src->ringbuffer == NULL) {
1054
        gst_audio_clock_reset (GST_AUDIO_CLOCK (src->clock), 0);
1055
1056
        src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
      }
1057
      GST_OBJECT_UNLOCK (src);
1058
      if (!gst_ring_buffer_open_device (src->ringbuffer))
1059
        goto open_failed;
Wim Taymans's avatar
Wim Taymans committed
1060
      break;
1061
    case GST_STATE_CHANGE_READY_TO_PAUSED:
1062
      GST_DEBUG_OBJECT (src, "READY->PAUSED");
1063
      src->next_sample = -1;
1064
      gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
1065
      gst_ring_buffer_may_start (src->ringbuffer, FALSE);
Wim Taymans's avatar
Wim Taymans committed
1066
      break;
1067
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1068
      GST_DEBUG_OBJECT (src, "PAUSED->PLAYING");
1069
      gst_ring_buffer_may_start (src->ringbuffer, TRUE);
Wim Taymans's avatar
Wim Taymans committed
1070
      break;
1071
1072
1073
1074
1075
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      GST_DEBUG_OBJECT (src, "PLAYING->PAUSED");
      gst_ring_buffer_may_start (src->ringbuffer, FALSE);
      gst_ring_buffer_pause (src->ringbuffer);
      break;
1076
    case GST_STATE_CHANGE_PAUSED_TO_READY:
1077
      GST_DEBUG_OBJECT (src, "PAUSED->READY");
1078
1079
      gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
      break;
Wim Taymans's avatar
Wim Taymans committed
1080
1081
1082
1083
    default:
      break;
  }

1084
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Wim Taymans's avatar
Wim Taymans committed
1085
1086

  switch (transition) {
1087
    case GST_STATE_CHANGE_PAUSED_TO_READY:
1088
      GST_DEBUG_OBJECT (src, "PAUSED->READY");
1089
      gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
1090
      break;
1091
    case GST_STATE_CHANGE_READY_TO_NULL:
1092
      GST_DEBUG_OBJECT (src, "READY->NULL");
1093
      gst_ring_buffer_close_device (src->ringbuffer);
1094
      GST_OBJECT_LOCK (src);
1095
      gst_object_unparent (GST_OBJECT_CAST (src->ringbuffer));
1096
      src->ringbuffer = NULL;
1097
      GST_OBJECT_UNLOCK (src);
Wim Taymans's avatar
Wim Taymans committed
1098
1099
1100
1101
1102
1103
      break;
    default:
      break;
  }

  return ret;
1104
1105
1106
1107
1108
1109
1110
1111
1112

  /* ERRORS */
open_failed:
  {
    /* subclass must post a meaningfull error message */
    GST_DEBUG_OBJECT (src, "open failed");
    return GST_STATE_CHANGE_FAILURE;
  }

Wim Taymans's avatar
Wim Taymans committed
1113
}