gstbaseaudiosrc.c 11.8 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
37
38
39
40
41
42
43
44
45
46

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

#define DEFAULT_BUFFER_TIME	500 * GST_USECOND
#define DEFAULT_LATENCY_TIME	10 * GST_USECOND
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
85
86
87
88
89
90
91
92
93
94
{
  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 =
95
      GST_DEBUG_FUNCPTR (gst_base_audio_src_set_property);
Wim Taymans's avatar
Wim Taymans committed
96
  gobject_class->get_property =
97
      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_property);
Wim Taymans's avatar
Wim Taymans committed
98
99
100
101
102
103
104
105
106
107
108

  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME,
      g_param_spec_int64 ("buffer-time", "Buffer Time",
          "Size of audio buffer in milliseconds (-1 = default)",
          -1, G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY_TIME,
      g_param_spec_int64 ("latency-time", "Latency Time",
          "Audio latency in milliseconds (-1 = default)",
          -1, G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));

  gstelement_class->change_state =
109
      GST_DEBUG_FUNCPTR (gst_base_audio_src_change_state);
110
111
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_base_audio_src_provide_clock);
Wim Taymans's avatar
Wim Taymans committed
112

113
114
115
116
  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
117

118
  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_base_audio_src_create);
Wim Taymans's avatar
Wim Taymans committed
119
120
121
}

static void
122
123
gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
    GstBaseAudioSrcClass * g_class)
Wim Taymans's avatar
Wim Taymans committed
124
125
126
127
128
{
  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;

  baseaudiosrc->clock = gst_audio_clock_new ("clock",
129
      (GstAudioClockGetTimeFunc) gst_base_audio_src_get_time, baseaudiosrc);
Wim Taymans's avatar
Wim Taymans committed
130
131

  gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc),
132
      gst_base_audio_src_fixate);
Wim Taymans's avatar
Wim Taymans committed
133
134
135
}

static GstClock *
136
gst_base_audio_src_provide_clock (GstElement * elem)
Wim Taymans's avatar
Wim Taymans committed
137
138
139
{
  GstBaseAudioSrc *src;

140
  src = GST_BASE_AUDIO_SRC (elem);
Wim Taymans's avatar
Wim Taymans committed
141
142
143
144
145

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

static GstClockTime
146
gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
147
148
149
150
151
152
153
{
  guint64 samples;
  GstClockTime result;

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

154
  samples = gst_ring_buffer_samples_done (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
155
156
157
158
159
160
161

  result = samples * GST_SECOND / src->ringbuffer->spec.rate;

  return result;
}

static void
162
gst_base_audio_src_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
163
164
165
166
    const GValue * value, GParamSpec * pspec)
{
  GstBaseAudioSrc *src;

167
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

  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
183
184
gst_base_audio_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
185
186
187
{
  GstBaseAudioSrc *src;

188
  src = GST_BASE_AUDIO_SRC (object);
Wim Taymans's avatar
Wim Taymans committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

  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
204
gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
205
206
207
208
209
210
211
212
213
214
{
  GstStructure *s;

  s = gst_caps_get_structure (caps, 0);

  gst_caps_structure_fixate_field_nearest_int (s, "rate", 44100);
  gst_caps_structure_fixate_field_nearest_int (s, "channels", 2);
  gst_caps_structure_fixate_field_nearest_int (s, "depth", 16);
  gst_caps_structure_fixate_field_nearest_int (s, "width", 16);
  gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
215
216
  if (gst_structure_has_field (s, "endianness"))
    gst_caps_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
Wim Taymans's avatar
Wim Taymans committed
217
218
219
}

static gboolean
220
gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
221
{
222
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
223
224
225
226
227
228
229
  GstRingBufferSpec *spec;

  spec = &src->ringbuffer->spec;

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

230
  if (!gst_ring_buffer_parse_caps (spec, caps))
Wim Taymans's avatar
Wim Taymans committed
231
232
233
234
235
236
237
238
239
    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");

240
  gst_ring_buffer_release (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
241

242
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
243
244
245

  GST_DEBUG ("acquire new ringbuffer");

246
  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
Wim Taymans's avatar
Wim Taymans committed
247
248
249
250
251
252
253
254
255
    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);

256
  gst_ring_buffer_debug_spec_buff (spec);
Wim Taymans's avatar
Wim Taymans committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

  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
274
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
Wim Taymans's avatar
Wim Taymans committed
275
276
277
278
279
280
281
282
283
284
    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
285
gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
286
{
287
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
Wim Taymans's avatar
Wim Taymans committed
288
289

  switch (GST_EVENT_TYPE (event)) {
290
291
    case GST_EVENT_FLUSH_START:
      gst_ring_buffer_pause (src->ringbuffer);
292
      gst_ring_buffer_clear_all (src->ringbuffer);
293
294
      break;
    case GST_EVENT_FLUSH_STOP:
295
296
297
      /* 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
298
299
300
301
302
303
304
305
      break;
    default:
      break;
  }
  return TRUE;
}

static GstFlowReturn
306
gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
Wim Taymans's avatar
Wim Taymans committed
307
{
308
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (psrc);
Wim Taymans's avatar
Wim Taymans committed
309
310
  GstBuffer *buf;
  guchar *data;
Wim Taymans's avatar
Wim Taymans committed
311
  guint len, samples;
Wim Taymans's avatar
Wim Taymans committed
312
  guint res;
313
  guint64 sample;
Wim Taymans's avatar
Wim Taymans committed
314

315
  if (!gst_ring_buffer_is_acquired (src->ringbuffer))
Wim Taymans's avatar
Wim Taymans committed
316
317
318
319
320
321
322
    goto wrong_state;

  buf = gst_buffer_new_and_alloc (src->ringbuffer->spec.segsize);

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

323
324
325
326
327
328
  if (src->next_sample != -1) {
    sample = src->next_sample;
  } else {
    sample = 0;
  }

Wim Taymans's avatar
Wim Taymans committed
329
330
331
  samples = len / src->ringbuffer->spec.bytes_per_sample;

  res = gst_ring_buffer_read (src->ringbuffer, sample, data, samples);
Wim Taymans's avatar
Wim Taymans committed
332
333
334
  if (res == -1)
    goto stopped;

Wim Taymans's avatar
Wim Taymans committed
335
  src->next_sample = sample + samples;
336

Wim Taymans's avatar
Wim Taymans committed
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  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 *
357
gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
Wim Taymans's avatar
Wim Taymans committed
358
359
360
361
{
  GstBaseAudioSrcClass *bclass;
  GstRingBuffer *buffer = NULL;

362
  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
Wim Taymans's avatar
Wim Taymans committed
363
364
365
366
367
368
369
370
371
372
373
  if (bclass->create_ringbuffer)
    buffer = bclass->create_ringbuffer (src);

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

  return buffer;
}

void
374
gst_base_audio_src_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
Wim Taymans's avatar
Wim Taymans committed
375
376
    gpointer user_data)
{
377
  //GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (data);
Wim Taymans's avatar
Wim Taymans committed
378
379
}

380
381
382
static GstStateChangeReturn
gst_base_audio_src_change_state (GstElement * element,
    GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
383
{
384
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
385
  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
Wim Taymans's avatar
Wim Taymans committed
386
387

  switch (transition) {
388
    case GST_STATE_CHANGE_NULL_TO_READY:
389
390
391
392
393
394
      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))
395
        return GST_STATE_CHANGE_FAILURE;
396
      src->next_sample = 0;
Wim Taymans's avatar
Wim Taymans committed
397
      break;
398
    case GST_STATE_CHANGE_READY_TO_PAUSED:
Wim Taymans's avatar
Wim Taymans committed
399
      break;
400
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
Wim Taymans's avatar
Wim Taymans committed
401
402
403
404
405
      break;
    default:
      break;
  }

406
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Wim Taymans's avatar
Wim Taymans committed
407
408

  switch (transition) {
409
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
410
      gst_ring_buffer_pause (src->ringbuffer);
Wim Taymans's avatar
Wim Taymans committed
411
      break;
412
    case GST_STATE_CHANGE_PAUSED_TO_READY:
413
      gst_ring_buffer_release (src->ringbuffer);
414
      src->next_sample = 0;
Wim Taymans's avatar
Wim Taymans committed
415
      break;
416
    case GST_STATE_CHANGE_READY_TO_NULL:
417
      gst_ring_buffer_close_device (src->ringbuffer);
418
      gst_object_unref (src->ringbuffer);
419
      src->ringbuffer = NULL;
Wim Taymans's avatar
Wim Taymans committed
420
421
422
423
424
425
426
      break;
    default:
      break;
  }

  return ret;
}