gstbaseaudiosrc.c 29 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
116
117
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);
#endif /* ENABLE_NLS */
}
Wim Taymans's avatar
Wim Taymans committed
118

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

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

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

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

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

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

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

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

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

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

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

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

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

186
187
188
189
190
191
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));

  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));

198
199
200
  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",
201
          DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202

203
204
205
206
207
208
  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
209
  gstelement_class->change_state =
210
      GST_DEBUG_FUNCPTR (gst_base_audio_src_change_state);
211
212
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_provide_clock);
Wim Taymans's avatar
Wim Taymans committed
213

214
215
  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_src_setcaps);
  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_src_event);
216
  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_base_audio_src_query);
217
218
  gstbasesrc_class->get_times =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_times);
219
220
221
  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);
222
  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_base_audio_src_fixate);
223
224
225
226

  /* 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);
227
  g_type_class_ref (GST_TYPE_RING_BUFFER);
Wim Taymans's avatar
Wim Taymans committed
228
229
230
}

static void
231
232
gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
    GstBaseAudioSrcClass * g_class)
Wim Taymans's avatar
Wim Taymans committed
233
{
234
235
  baseaudiosrc->priv = GST_BASE_AUDIO_SRC_GET_PRIVATE (baseaudiosrc);

Wim Taymans's avatar
Wim Taymans committed
236
237
  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;
238
  baseaudiosrc->priv->provide_clock = DEFAULT_PROVIDE_CLOCK;
239
  baseaudiosrc->priv->slave_method = DEFAULT_SLAVE_METHOD;
240
241
242
  /* 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
243

244
  baseaudiosrc->clock = gst_audio_clock_new ("GstAudioSrcClock",
245
      (GstAudioClockGetTimeFunc) gst_base_audio_src_get_time, baseaudiosrc);
Wim Taymans's avatar
Wim Taymans committed
246

247
  /* we are always a live source */
248
  gst_base_src_set_live (GST_BASE_SRC (baseaudiosrc), TRUE);
Wim Taymans's avatar
Wim Taymans committed
249
  /* we operate in time */
250
  gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME);
Wim Taymans's avatar
Wim Taymans committed
251
252
}

253
254
255
256
257
258
259
static void
gst_base_audio_src_dispose (GObject * object)
{
  GstBaseAudioSrc *src;

  src = GST_BASE_AUDIO_SRC (object);

260
  GST_OBJECT_LOCK (src);
261
262
263
264
265
266
267
268
  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;
  }
269
  GST_OBJECT_UNLOCK (src);
270
271
272
273

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

Wim Taymans's avatar
Wim Taymans committed
274
static GstClock *
275
gst_base_audio_src_provide_clock (GstElement * elem)
Wim Taymans's avatar
Wim Taymans committed
276
277
{
  GstBaseAudioSrc *src;
278
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
279

280
  src = GST_BASE_AUDIO_SRC (elem);
Wim Taymans's avatar
Wim Taymans committed
281

282
283
284
285
286
287
288
  /* 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;

289
290
291
292
  GST_OBJECT_LOCK (src);
  if (!src->priv->provide_clock)
    goto clock_disabled;

293
  clock = GST_CLOCK_CAST (gst_object_ref (src->clock));
294
  GST_OBJECT_UNLOCK (src);
295
296
297
298
299
300
301
302
303

  return clock;

  /* ERRORS */
wrong_state:
  {
    GST_DEBUG_OBJECT (src, "ringbuffer not acquired");
    return NULL;
  }
304
305
306
307
308
309
clock_disabled:
  {
    GST_DEBUG_OBJECT (src, "clock provide disabled");
    GST_OBJECT_UNLOCK (src);
    return NULL;
  }
Wim Taymans's avatar
Wim Taymans committed
310
311
312
}

static GstClockTime
313
gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
314
{
315
316
  guint64 raw, samples;
  guint delay;
Wim Taymans's avatar
Wim Taymans committed
317
318
  GstClockTime result;

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

322
323
324
325
326
327
328
  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
329

330
331
  result = gst_util_uint64_scale_int (samples, GST_SECOND,
      src->ringbuffer->spec.rate);
Wim Taymans's avatar
Wim Taymans committed
332

333
334
335
336
  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
337
338
339
  return result;
}

340
341
342
343
344
345
346
347
348
349
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;
}

350
351
352
353
354
355
356
357
358
359
360
361
362
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
/**
 * 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;
}

396
397
398
399
400
401
402
403
404
405
406
407
408
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
/**
 * 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
440
static void
441
gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
442
443
444
445
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSrc *src;

446
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
447
448
449
450
451
452
453
454

  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;
455
    case PROP_PROVIDE_CLOCK:
456
      gst_base_audio_src_set_provide_clock (src, g_value_get_boolean (value));
457
      break;
458
459
460
    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
461
462
463
464
465
466
467
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
468
469
gst_base_audio_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
470
471
472
{
  GstBaseAudioSrc *src;

473
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
474
475
476
477
478
479
480
481

  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;
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
    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;
498
    case PROP_PROVIDE_CLOCK:
499
      g_value_set_boolean (value, gst_base_audio_src_get_provide_clock (src));
500
      break;
501
502
503
    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
504
505
506
507
508
509
510
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
511
gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
512
513
{
  GstStructure *s;
514
  gint width, depth;
Wim Taymans's avatar
Wim Taymans committed
515
516
517

  s = gst_caps_get_structure (caps, 0);

518
  /* fields for all formats */
519
520
521
  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);
522
523
524
525
526
527
528
529
530
531

  /* 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);
532
  if (gst_structure_has_field (s, "endianness"))
533
    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
Wim Taymans's avatar
Wim Taymans committed
534
535
536
}

static gboolean
537
gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
538
{
539
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
540
541
542
543
544
545
546
  GstRingBufferSpec *spec;

  spec = &src->ringbuffer->spec;

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

547
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
548
549
550
551
552
553
554
555
556
    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");

557
  gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
558

559
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
560
561
562

  GST_DEBUG ("acquire new ringbuffer");

563
  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
Wim Taymans's avatar
Wim Taymans committed
564
565
566
567
568
569
570
571
572
    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);

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

575
576
577
  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
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  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
594
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
595
596
    GstClockTime * start, GstClockTime * end)
{
597
598
  /* 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
599
600
601
602
  *start = GST_CLOCK_TIME_NONE;
  *end = GST_CLOCK_TIME_NONE;
}

603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
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;

621
      /* we have at least 1 segment of latency */
622
623
624
      min_latency =
          gst_util_uint64_scale_int (spec->segsize, GST_SECOND,
          spec->rate * spec->bytes_per_sample);
625
      /* we cannot delay more than the buffersize else we lose data */
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
      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
649
static gboolean
650
gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
651
{
652
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
653
654

  switch (GST_EVENT_TYPE (event)) {
655
656
    case GST_EVENT_FLUSH_START:
      gst_ring_buffer_pause (src->ringbuffer);
657
      gst_ring_buffer_clear_all (src->ringbuffer);
658
659
      break;
    case GST_EVENT_FLUSH_STOP:
660
661
662
      /* 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
663
664
665
666
667
668
669
      break;
    default:
      break;
  }
  return TRUE;
}

670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
/* 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;

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

699
700
701
702
703
  /* 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) {
704
    GST_DEBUG_OBJECT (src, "dropped, align to segment %d", segdone);
705
706
707
708
709
710
711
    /* sample would be dropped, position to next playable position */
    sample = (segdone - segtotal + 1) * sps;
  }

  return sample;
}

Wim Taymans's avatar
Wim Taymans committed
712
static GstFlowReturn
713
714
gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
    GstBuffer ** outbuf)
Wim Taymans's avatar
Wim Taymans committed
715
{
716
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
717
718
  GstBuffer *buf;
  guchar *data;
719
  guint samples, total_samples;
720
  guint64 sample;
721
  gint bps;
722
  GstRingBuffer *ringbuffer;
723
  GstRingBufferSpec *spec;
724
  guint read;
725
  GstClockTime timestamp, duration;
726
  GstClock *clock;
727
728

  ringbuffer = src->ringbuffer;
729
  spec = &ringbuffer->spec;
Wim Taymans's avatar
Wim Taymans committed
730

731
  if (G_UNLIKELY (!gst_ring_buffer_is_acquired (ringbuffer)))
Wim Taymans's avatar
Wim Taymans committed
732
733
    goto wrong_state;

734
  bps = spec->bytes_per_sample;
Wim Taymans's avatar
Wim Taymans committed
735

736
737
  if ((length == 0 && bsrc->blocksize == 0) || length == -1)
    /* no length given, use the default segment size */
738
    length = spec->segsize;
739
740
741
  else
    /* make sure we round down to an integral number of samples */
    length -= length % bps;
Wim Taymans's avatar
Wim Taymans committed
742

743
  /* figure out the offset in the ringbuffer */
744
  if (G_UNLIKELY (offset != -1)) {
745
746
747
748
    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)
749
      goto wrong_offset;
750
751
752
753
  } 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);
754
755
  }

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

758
  /* get the number of samples to read */
759
  total_samples = samples = length / bps;
760
761
762
763

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

765
766
767
768
769
770
771
772
  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. */
773
    GST_DEBUG_OBJECT (src, "wait playing");
774
775
776
    if (gst_base_src_wait_playing (bsrc) != GST_FLOW_OK)
      goto stopped;

777
778
    GST_DEBUG_OBJECT (src, "continue playing");

779
780
781
782
783
    /* read next samples */
    sample += read;
    samples -= read;
    data += read * bps;
  } while (TRUE);
Wim Taymans's avatar
Wim Taymans committed
784

785
786
787
788
789
  /* 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);
790
791
    GST_ELEMENT_WARNING (src, CORE, CLOCK,
        (_("Can't record audio fast enough")),
792
        ("dropped %" G_GUINT64_FORMAT " samples", sample - src->next_sample));
793
794
795
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
  }

796
797
798
799
800
801
802
  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;

803
804
  GST_OBJECT_LOCK (src);
  clock = GST_ELEMENT_CLOCK (src);
805
  if (clock != NULL && clock != src->clock) {
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
    switch (src->priv->slave_method) {
      case GST_BASE_AUDIO_SRC_SLAVE_RESAMPLE:
        /* not implemented, use retimestamp algorithm. This algorithm should
         * work on the readout pointer and produces more or less samples based
         * on the clock drift */
      case GST_BASE_AUDIO_SRC_SLAVE_SKEW:
        /* not implemented, use retimestamp algorithm. This algortihm should work
         * on the readout pointer above and creates small jumps when needed. */
      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;
    }
840
841
  }
  GST_OBJECT_UNLOCK (src);
Wim Taymans's avatar
Wim Taymans committed
842
843

  GST_BUFFER_TIMESTAMP (buf) = timestamp;
844
  GST_BUFFER_DURATION (buf) = duration;
845
846
  GST_BUFFER_OFFSET (buf) = sample;
  GST_BUFFER_OFFSET_END (buf) = sample + samples;
847

Wim Taymans's avatar
Wim Taymans committed
848
849
850
851
  *outbuf = buf;

  return GST_FLOW_OK;

852
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
853
854
wrong_state:
  {
855
    GST_DEBUG_OBJECT (src, "ringbuffer in wrong state");
Wim Taymans's avatar
Wim Taymans committed
856
857
    return GST_FLOW_WRONG_STATE;
  }
858
859
860
861
862
863
864
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
865
866
867
stopped:
  {
    gst_buffer_unref (buf);
868
    GST_DEBUG_OBJECT (src, "ringbuffer stopped");
Wim Taymans's avatar
Wim Taymans committed
869
870
871
872
    return GST_FLOW_WRONG_STATE;
  }
}

Wim Taymans's avatar
Wim Taymans committed
873
874
875
876
877
878
879
880
881
882
/**
 * 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
883
GstRingBuffer *
884
gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
885
886
887
888
{
  GstBaseAudioSrcClass *bclass;
  GstRingBuffer *buffer = NULL;

889
  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
Wim Taymans's avatar
Wim Taymans committed
890
891
892
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (src);

893
894
  if (G_LIKELY (buffer))
    gst_object_set_parent (GST_OBJECT_CAST (buffer), GST_OBJECT_CAST (src));
Wim Taymans's avatar
Wim Taymans committed
895
896
897
898

  return buffer;
}

899
900
901
static GstStateChangeReturn
gst_base_audio_src_change_state (GstElement * element,
    GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
902
{
903
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
904
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
Wim Taymans's avatar
Wim Taymans committed
905
906

  switch (transition) {
907
    case GST_STATE_CHANGE_NULL_TO_READY:
908
      GST_DEBUG_OBJECT (src, "NULL->READY");
909
      GST_OBJECT_LOCK (src);
910
      if (src->ringbuffer == NULL) {
911
        gst_audio_clock_reset (GST_AUDIO_CLOCK (src->clock), 0);
912
913
        src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
      }
914
      GST_OBJECT_UNLOCK (src);
915
      if (!gst_ring_buffer_open_device (src->ringbuffer))
916
        goto open_failed;
Wim Taymans's avatar
Wim Taymans committed
917
      break;
918
    case GST_STATE_CHANGE_READY_TO_PAUSED:
919
      GST_DEBUG_OBJECT (src, "READY->PAUSED");
920
      src->next_sample = -1;
921
      gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
922
      gst_ring_buffer_may_start (src->ringbuffer, FALSE);
Wim Taymans's avatar
Wim Taymans committed
923
      break;
924
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
925
      GST_DEBUG_OBJECT (src, "PAUSED->PLAYING");
926
      gst_ring_buffer_may_start (src->ringbuffer, TRUE);
Wim Taymans's avatar
Wim Taymans committed
927
      break;
928
929
930
931
932
    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;
933
    case GST_STATE_CHANGE_PAUSED_TO_READY:
934
      GST_DEBUG_OBJECT (src, "PAUSED->READY");
935
936
      gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
      break;
Wim Taymans's avatar
Wim Taymans committed
937
938
939
940
    default:
      break;
  }

941
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Wim Taymans's avatar
Wim Taymans committed
942
943

  switch (transition) {
944
    case GST_STATE_CHANGE_PAUSED_TO_READY:
945
      GST_DEBUG_OBJECT (src, "PAUSED->READY");
946
      gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
947
      break;
948
    case GST_STATE_CHANGE_READY_TO_NULL:
949
      GST_DEBUG_OBJECT (src, "READY->NULL");
950
      gst_ring_buffer_close_device (src->ringbuffer);
951
      GST_OBJECT_LOCK (src);
952
      gst_object_unparent (GST_OBJECT_CAST (src->ringbuffer));
953
      src->ringbuffer = NULL;
954
      GST_OBJECT_UNLOCK (src);
Wim Taymans's avatar
Wim Taymans committed
955
956
957
958
959
960
      break;
    default:
      break;
  }

  return ret;
961
962
963
964
965
966
967
968
969

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