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