gstbaseaudiosrc.c 12.5 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
23
24
25
26
/* 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.
 */

#include <string.h>

#include "gstbaseaudiosrc.h"

27
28
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
29
30
31
32
33
34
35
36

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37
38
#define DEFAULT_BUFFER_TIME     500 * GST_USECOND
#define DEFAULT_LATENCY_TIME    10 * GST_USECOND
Wim Taymans's avatar
Wim Taymans committed
39
40
41
42
43
44
45
46
enum
{
  PROP_0,
  PROP_BUFFER_TIME,
  PROP_LATENCY_TIME,
};

#define _do_init(bla) \
47
    GST_DEBUG_CATEGORY_INIT (gst_base_audio_src_debug, "baseaudiosrc", 0, "baseaudiosrc element");
Wim Taymans's avatar
Wim Taymans committed
48

49
GST_BOILERPLATE_FULL (GstBaseAudioSrc, gst_base_audio_src, GstPushSrc,
50
    GST_TYPE_PUSH_SRC, _do_init);
Wim Taymans's avatar
Wim Taymans committed
51

52
static void gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
53
    const GValue * value, GParamSpec * pspec);
54
static void gst_base_audio_src_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
55
56
    GValue * value, GParamSpec * pspec);

57
static void gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
58

59
60
static GstStateChangeReturn gst_base_audio_src_change_state (GstElement *
    element, GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
61

62
static GstClock *gst_base_audio_src_provide_clock (GstElement * elem);
63
static GstClockTime gst_base_audio_src_get_time (GstClock * clock,
Wim Taymans's avatar
Wim Taymans committed
64
65
    GstBaseAudioSrc * src);

66
static GstFlowReturn gst_base_audio_src_create (GstPushSrc * psrc,
Wim Taymans's avatar
Wim Taymans committed
67
68
    GstBuffer ** buf);

69
70
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
71
    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
72
static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
73

74
//static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 };
Wim Taymans's avatar
Wim Taymans committed
75
76

static void
77
gst_base_audio_src_base_init (gpointer g_class)
Wim Taymans's avatar
Wim Taymans committed
78
79
80
81
{
}

static void
82
gst_base_audio_src_class_init (GstBaseAudioSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
83
{
84
  gchar *longdesc;
Wim Taymans's avatar
Wim Taymans committed
85
86
87
88
89
90
91
92
93
94
95
  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;

  gobject_class->set_property =
96
      GST_DEBUG_FUNCPTR (gst_base_audio_src_set_property);
Wim Taymans's avatar
Wim Taymans committed
97
  gobject_class->get_property =
98
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_property);
Wim Taymans's avatar
Wim Taymans committed
99

100
101
102
  longdesc =
      g_strdup_printf
      ("Size of audio buffer in microseconds (use -1 for default of %"
103
      G_GUINT64_FORMAT " us)", DEFAULT_BUFFER_TIME / GST_USECOND);
Wim Taymans's avatar
Wim Taymans committed
104
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME,
105
106
107
108
109
      g_param_spec_int64 ("buffer-time", "Buffer Time", longdesc, -1,
          G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
  g_free (longdesc);
  longdesc =
      g_strdup_printf ("Audio latency in microseconds (use -1 for default of %"
110
      G_GUINT64_FORMAT " us)", DEFAULT_LATENCY_TIME / GST_USECOND);
Wim Taymans's avatar
Wim Taymans committed
111
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY_TIME,
112
113
114
      g_param_spec_int64 ("latency-time", "Latency Time", longdesc, -1,
          G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));
  g_free (longdesc);
Wim Taymans's avatar
Wim Taymans committed
115
116

  gstelement_class->change_state =
117
      GST_DEBUG_FUNCPTR (gst_base_audio_src_change_state);
118
119
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_provide_clock);
Wim Taymans's avatar
Wim Taymans committed
120

121
122
123
124
  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_src_setcaps);
  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_src_event);
  gstbasesrc_class->get_times =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_times);
Wim Taymans's avatar
Wim Taymans committed
125

126
  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_base_audio_src_create);
Wim Taymans's avatar
Wim Taymans committed
127
128
129
}

static void
130
131
gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
    GstBaseAudioSrcClass * g_class)
Wim Taymans's avatar
Wim Taymans committed
132
133
134
135
136
{
  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;

  baseaudiosrc->clock = gst_audio_clock_new ("clock",
137
      (GstAudioClockGetTimeFunc) gst_base_audio_src_get_time, baseaudiosrc);
Wim Taymans's avatar
Wim Taymans committed
138
139

  gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc),
140
      gst_base_audio_src_fixate);
141
142

  gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME);
Wim Taymans's avatar
Wim Taymans committed
143
144
145
}

static GstClock *
146
gst_base_audio_src_provide_clock (GstElement * elem)
Wim Taymans's avatar
Wim Taymans committed
147
148
149
{
  GstBaseAudioSrc *src;

150
  src = GST_BASE_AUDIO_SRC (elem);
Wim Taymans's avatar
Wim Taymans committed
151
152
153
154
155

  return GST_CLOCK (gst_object_ref (GST_OBJECT (src->clock)));
}

static GstClockTime
156
gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
157
158
159
160
161
162
163
{
  guint64 samples;
  GstClockTime result;

  if (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0)
    return 0;

164
  samples = gst_ring_buffer_samples_done (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
165

166
167
  result = gst_util_uint64_scale_int (samples, GST_SECOND,
      src->ringbuffer->spec.rate);
Wim Taymans's avatar
Wim Taymans committed
168
169
170
171
172

  return result;
}

static void
173
gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
174
175
176
177
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSrc *src;

178
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

  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;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
194
195
gst_base_audio_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
196
197
198
{
  GstBaseAudioSrc *src;

199
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

  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;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
215
gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
216
217
218
219
220
{
  GstStructure *s;

  s = gst_caps_get_structure (caps, 0);

221
222
223
224
  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, "depth", 16);
  gst_structure_fixate_field_nearest_int (s, "width", 16);
Wim Taymans's avatar
Wim Taymans committed
225
  gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
226
  if (gst_structure_has_field (s, "endianness"))
227
    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
Wim Taymans's avatar
Wim Taymans committed
228
229
230
}

static gboolean
231
gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
232
{
233
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
234
235
236
237
238
239
240
  GstRingBufferSpec *spec;

  spec = &src->ringbuffer->spec;

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

241
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
242
243
244
245
246
247
248
249
250
    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");

251
  gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
252

253
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
254
255
256

  GST_DEBUG ("acquire new ringbuffer");

257
  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
Wim Taymans's avatar
Wim Taymans committed
258
259
260
261
262
263
264
265
266
    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);

267
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

  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
285
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
286
287
288
289
290
291
292
293
294
295
    GstClockTime * start, GstClockTime * end)
{
  /* ne need to sync to a clock here, we schedule the samples based
   * on our own clock for the moment. FIXME, implement this when
   * we are not using our own clock */
  *start = GST_CLOCK_TIME_NONE;
  *end = GST_CLOCK_TIME_NONE;
}

static gboolean
296
gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
297
{
298
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
299
300

  switch (GST_EVENT_TYPE (event)) {
301
302
    case GST_EVENT_FLUSH_START:
      gst_ring_buffer_pause (src->ringbuffer);
303
      gst_ring_buffer_clear_all (src->ringbuffer);
304
305
      break;
    case GST_EVENT_FLUSH_STOP:
306
307
308
      /* 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
309
310
311
312
313
314
315
316
      break;
    default:
      break;
  }
  return TRUE;
}

static GstFlowReturn
317
gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
Wim Taymans's avatar
Wim Taymans committed
318
{
319
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (psrc);
Wim Taymans's avatar
Wim Taymans committed
320
321
  GstBuffer *buf;
  guchar *data;
Wim Taymans's avatar
Wim Taymans committed
322
  guint len, samples;
Wim Taymans's avatar
Wim Taymans committed
323
  guint res;
324
  guint64 sample;
325
326
327
  GstRingBuffer *ringbuffer;

  ringbuffer = src->ringbuffer;
Wim Taymans's avatar
Wim Taymans committed
328

329
  if (!gst_ring_buffer_is_acquired (ringbuffer))
Wim Taymans's avatar
Wim Taymans committed
330
331
    goto wrong_state;

332
  buf = gst_buffer_new_and_alloc (ringbuffer->spec.segsize);
Wim Taymans's avatar
Wim Taymans committed
333
334
335
336

  data = GST_BUFFER_DATA (buf);
  len = GST_BUFFER_SIZE (buf);

337
338
339
340
341
342
  if (src->next_sample != -1) {
    sample = src->next_sample;
  } else {
    sample = 0;
  }

343
  samples = len / ringbuffer->spec.bytes_per_sample;
Wim Taymans's avatar
Wim Taymans committed
344

345
  res = gst_ring_buffer_read (ringbuffer, sample, data, samples);
Wim Taymans's avatar
Wim Taymans committed
346
347
348
  if (res == -1)
    goto stopped;

349
350
  GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (sample,
      GST_SECOND, ringbuffer->spec.rate);
Wim Taymans's avatar
Wim Taymans committed
351
  src->next_sample = sample + samples;
352
353
  GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (src->next_sample,
      GST_SECOND, ringbuffer->spec.rate) - GST_BUFFER_TIMESTAMP (buf);
354

Wim Taymans's avatar
Wim Taymans committed
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc)));

  *outbuf = buf;

  return GST_FLOW_OK;

wrong_state:
  {
    GST_DEBUG ("ringbuffer in wrong state");
    return GST_FLOW_WRONG_STATE;
  }
stopped:
  {
    gst_buffer_unref (buf);
    GST_DEBUG ("ringbuffer stopped");
    return GST_FLOW_WRONG_STATE;
  }
}

GstRingBuffer *
375
gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
376
377
378
379
{
  GstBaseAudioSrcClass *bclass;
  GstRingBuffer *buffer = NULL;

380
  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
Wim Taymans's avatar
Wim Taymans committed
381
382
383
384
385
386
387
388
389
390
391
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (src);

  if (buffer) {
    gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (src));
  }

  return buffer;
}

void
392
gst_base_audio_src_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
Wim Taymans's avatar
Wim Taymans committed
393
394
    gpointer user_data)
{
395
  //GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (data);
Wim Taymans's avatar
Wim Taymans committed
396
397
}

398
399
400
static GstStateChangeReturn
gst_base_audio_src_change_state (GstElement * element,
    GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
401
{
402
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
403
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
Wim Taymans's avatar
Wim Taymans committed
404
405

  switch (transition) {
406
    case GST_STATE_CHANGE_NULL_TO_READY:
407
408
409
410
411
412
      if (src->ringbuffer == NULL) {
        src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
        gst_ring_buffer_set_callback (src->ringbuffer,
            gst_base_audio_src_callback, src);
      }
      if (!gst_ring_buffer_open_device (src->ringbuffer))
413
        return GST_STATE_CHANGE_FAILURE;
414
      src->next_sample = 0;
Wim Taymans's avatar
Wim Taymans committed
415
      break;
416
    case GST_STATE_CHANGE_READY_TO_PAUSED:
417
      gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
Wim Taymans's avatar
Wim Taymans committed
418
      break;
419
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
Wim Taymans's avatar
Wim Taymans committed
420
421
422
423
424
      break;
    default:
      break;
  }

425
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Wim Taymans's avatar
Wim Taymans committed
426
427

  switch (transition) {
428
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
429
      gst_ring_buffer_pause (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
430
      break;
431
    case GST_STATE_CHANGE_PAUSED_TO_READY:
432
      gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
433
      gst_ring_buffer_release (src->ringbuffer);
434
      src->next_sample = 0;
Wim Taymans's avatar
Wim Taymans committed
435
      break;
436
    case GST_STATE_CHANGE_READY_TO_NULL:
437
      gst_ring_buffer_close_device (src->ringbuffer);
438
      gst_object_unref (src->ringbuffer);
439
      src->ringbuffer = NULL;
Wim Taymans's avatar
Wim Taymans committed
440
441
442
443
444
445
446
      break;
    default:
      break;
  }

  return ret;
}