gstbaseaudiosrc.c 33.8 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#define GST_TYPE_SLAVE_METHOD (slave_method_get_type ())

static GType
slave_method_get_type (void)
{
  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;
}

69
70
71
72
73
74
#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;
75
76
77

  /* the clock slaving algorithm in use */
  GstBaseAudioSrcSlaveMethod slave_method;
78
79
};

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

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

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

106
107
108
109
110
111
112
113
114
115
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);
116
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
117
118
#endif /* ENABLE_NLS */
}
Wim Taymans's avatar
Wim Taymans committed
119

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

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

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

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

136
137
138
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
139

140
141
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
142
    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
143
static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
144
static gboolean gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query);
145
static void gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
146

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

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

static void
155
gst_base_audio_src_class_init (GstBaseAudioSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
156
157
158
159
160
161
162
163
164
165
166
{
  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;

167
168
  g_type_class_add_private (klass, sizeof (GstBaseAudioSrcPrivate));

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

175
176
177
  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,
178
179
          G_MAXINT64, DEFAULT_BUFFER_TIME,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180
181
182
183

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

187
188
189
190
191
192
193
  /**
   * GstBaseAudioSrc:actual-buffer-time:
   *
   * Actual configured size of audio buffer in microseconds.
   *
   * Since: 0.10.20
   **/
194
195
196
197
198
199
  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));

200
201
202
203
204
205
206
  /**
   * GstBaseAudioSrc:actual-latency-time:
   *
   * Actual configured audio latency in microseconds.
   *
   * Since: 0.10.20
   **/
207
208
209
210
211
212
  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));

213
214
215
  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",
216
          DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217

218
219
220
221
222
223
  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",
          GST_TYPE_SLAVE_METHOD, DEFAULT_SLAVE_METHOD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

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

229
230
  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_src_setcaps);
  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_src_event);
231
  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_base_audio_src_query);
232
233
  gstbasesrc_class->get_times =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_times);
234
235
236
  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);
237
  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_base_audio_src_fixate);
238
239
240
241

  /* 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);
242
  g_type_class_ref (GST_TYPE_RING_BUFFER);
Wim Taymans's avatar
Wim Taymans committed
243
244
245
}

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

Wim Taymans's avatar
Wim Taymans committed
251
252
  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;
253
  baseaudiosrc->priv->provide_clock = DEFAULT_PROVIDE_CLOCK;
254
  baseaudiosrc->priv->slave_method = DEFAULT_SLAVE_METHOD;
255
256
257
  /* 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
258

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

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

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

  src = GST_BASE_AUDIO_SRC (object);

275
  GST_OBJECT_LOCK (src);
276
277
278
279
280
281
282
283
  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;
  }
284
  GST_OBJECT_UNLOCK (src);
285
286
287
288

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

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

295
  src = GST_BASE_AUDIO_SRC (elem);
Wim Taymans's avatar
Wim Taymans committed
296

297
298
299
300
301
302
303
  /* 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;

304
305
306
307
  GST_OBJECT_LOCK (src);
  if (!src->priv->provide_clock)
    goto clock_disabled;

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

  return clock;

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

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

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

337
338
339
340
341
342
343
  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
344

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

348
349
350
351
  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
352
353
354
  return result;
}

355
356
357
358
359
360
361
362
363
364
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;
}

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
409
410
/**
 * 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;
}

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
453
454
/**
 * 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
455
static void
456
gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
457
458
459
460
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSrc *src;

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

  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;
470
    case PROP_PROVIDE_CLOCK:
471
      gst_base_audio_src_set_provide_clock (src, g_value_get_boolean (value));
472
      break;
473
474
475
    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
476
477
478
479
480
481
482
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

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

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

  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;
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    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;
513
    case PROP_PROVIDE_CLOCK:
514
      g_value_set_boolean (value, gst_base_audio_src_get_provide_clock (src));
515
      break;
516
517
518
    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
519
520
521
522
523
524
525
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

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

  s = gst_caps_get_structure (caps, 0);

533
  /* fields for all formats */
534
535
536
  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);
537
538
539
540
541
542
543
544
545
546

  /* 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);
547
  if (gst_structure_has_field (s, "endianness"))
548
    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
Wim Taymans's avatar
Wim Taymans committed
549
550
551
}

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

  spec = &src->ringbuffer->spec;

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

562
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
563
564
565
566
567
568
569
570
571
    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");

572
  gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
573

574
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
575
576
577

  GST_DEBUG ("acquire new ringbuffer");

578
  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
Wim Taymans's avatar
Wim Taymans committed
579
580
581
582
583
584
585
586
587
    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);

588
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
589

590
591
592
  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
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
  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
609
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
610
611
    GstClockTime * start, GstClockTime * end)
{
612
613
  /* 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
614
615
616
617
  *start = GST_CLOCK_TIME_NONE;
  *end = GST_CLOCK_TIME_NONE;
}

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
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;

636
      /* we have at least 1 segment of latency */
637
638
639
      min_latency =
          gst_util_uint64_scale_int (spec->segsize, GST_SECOND,
          spec->rate * spec->bytes_per_sample);
640
      /* we cannot delay more than the buffersize else we lose data */
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
      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
664
static gboolean
665
gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
666
{
667
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
668
669

  switch (GST_EVENT_TYPE (event)) {
670
671
    case GST_EVENT_FLUSH_START:
      gst_ring_buffer_pause (src->ringbuffer);
672
      gst_ring_buffer_clear_all (src->ringbuffer);
673
674
      break;
    case GST_EVENT_FLUSH_STOP:
675
676
677
      /* 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
678
679
680
681
682
683
684
      break;
    default:
      break;
  }
  return TRUE;
}

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
710
711
/* 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;

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

714
715
716
717
718
  /* 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) {
719
    GST_DEBUG_OBJECT (src, "dropped, align to segment %d", segdone);
720
721
722
723
724
725
726
    /* sample would be dropped, position to next playable position */
    sample = (segdone - segtotal + 1) * sps;
  }

  return sample;
}

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

  ringbuffer = src->ringbuffer;
744
  spec = &ringbuffer->spec;
Wim Taymans's avatar
Wim Taymans committed
745

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

749
  bps = spec->bytes_per_sample;
Wim Taymans's avatar
Wim Taymans committed
750

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

758
  /* figure out the offset in the ringbuffer */
759
  if (G_UNLIKELY (offset != -1)) {
760
761
762
763
    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)
764
      goto wrong_offset;
765
766
767
768
  } 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);
769
770
  }

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

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

  /* 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
779

780
781
782
783
784
785
786
787
  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. */
788
    GST_DEBUG_OBJECT (src, "wait playing");
789
790
791
    if (gst_base_src_wait_playing (bsrc) != GST_FLOW_OK)
      goto stopped;

792
793
    GST_DEBUG_OBJECT (src, "continue playing");

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

800
801
802
803
804
  /* 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);
805
806
    GST_ELEMENT_WARNING (src, CORE, CLOCK,
        (_("Can't record audio fast enough")),
807
        ("dropped %" G_GUINT64_FORMAT " samples", 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
865
866
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
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
      {
        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;

        GST_DEBUG_OBJECT (bsrc, "\n running_time = % " GST_TIME_FORMAT
            "\n timestamp     = % " GST_TIME_FORMAT
            "\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 */
          new_sample = new_first_segment * sps;

          /* 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: "
              "Updating the timestamp to % " GST_TIME_FORMAT ", "
              "and src->next_sample to % " G_GUINT64_FORMAT, segment_diff,
              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
971
972
973
974
975
976
977
  } else {
    GstClockTime base_time;

    /* 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;
978
  }
979
980

no_sync:
981
  GST_OBJECT_UNLOCK (src);
Wim Taymans's avatar
Wim Taymans committed
982
983

  GST_BUFFER_TIMESTAMP (buf) = timestamp;
984
  GST_BUFFER_DURATION (buf) = duration;
985
986
  GST_BUFFER_OFFSET (buf) = sample;
  GST_BUFFER_OFFSET_END (buf) = sample + samples;
987

Wim Taymans's avatar
Wim Taymans committed
988
989
990
991
  *outbuf = buf;

  return GST_FLOW_OK;

992
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
993
994
wrong_state:
  {
995
    GST_DEBUG_OBJECT (src, "ringbuffer in wrong state");
Wim Taymans's avatar
Wim Taymans committed
996
997
    return GST_FLOW_WRONG_STATE;
  }
998
999
1000
1001
1002
1003
1004
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
1005
1006
1007
stopped:
  {
    gst_buffer_unref (buf);
1008
    GST_DEBUG_OBJECT (src, "ringbuffer stopped");
Wim Taymans's avatar
Wim Taymans committed
1009
1010
1011
1012
    return GST_FLOW_WRONG_STATE;
  }
}

Wim Taymans's avatar
Wim Taymans committed
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
/**
 * 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
1023
GstRingBuffer *
1024
gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
1025
1026
1027
1028
{
  GstBaseAudioSrcClass *bclass;
  GstRingBuffer *buffer = NULL;

1029
  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
Wim Taymans's avatar
Wim Taymans committed
1030
1031
1032
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (src);

1033
1034
  if (G_LIKELY (buffer))
    gst_object_set_parent (GST_OBJECT_CAST (buffer), GST_OBJECT_CAST (src));
Wim Taymans's avatar
Wim Taymans committed
1035
1036
1037
1038

  return buffer;
}

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

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

1081
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Wim Taymans's avatar
Wim Taymans committed
1082
1083

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

  return ret;
1101
1102
1103
1104
1105
1106
1107
1108
1109

  /* 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
1110
}