gstalsasrc.c 23.7 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
3
/* GStreamer
 * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
4
 * gstalsasrc.c:
5
6
7
8
9
10
11
12
 *
 * 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
Wim Taymans's avatar
Wim Taymans committed
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
15
16
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Wim Taymans's avatar
Wim Taymans committed
17
18
19
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
20
21
 */

Wim Taymans's avatar
Wim Taymans committed
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * SECTION:element-alsasrc
 * @short_description: capture audio from an alsa device
 * @see_also: alsasink, alsamixer
 *
 * <refsect2>
 * <para>
 * This element reads data from an audio card using the ALSA API.
 * </para>
 * <title>Example pipelines</title>
 * <para>
 * Record from a sound card using ALSA and encode to Ogg/Vorbis.
 * </para>
 * <programlisting>
 * gst-launch -v alsasrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
 * </programlisting>
 * </refsect2>
 *
 * Last reviewed on 2006-03-01 (0.10.4)
 */

43
44
45
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Wim Taymans's avatar
Wim Taymans committed
46
47
48
49
50
51
52
53
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <alsa/asoundlib.h>

54
#include "gstalsasrc.h"
55
#include "gstalsadeviceprobe.h"
56

57
58
#include <gst/gst-i18n-plugin.h>

59
/* elementfactory information */
Stefan Kost's avatar
Stefan Kost committed
60
static const GstElementDetails gst_alsasrc_details =
j^'s avatar
j^ committed
61
GST_ELEMENT_DETAILS ("Audio source (ALSA)",
62
    "Source/Audio",
63
    "Read from a sound card via ALSA",
Wim Taymans's avatar
Wim Taymans committed
64
65
    "Wim Taymans <wim@fluendo.com>");

Wim Taymans's avatar
Wim Taymans committed
66
67
68
#define DEFAULT_PROP_DEVICE		"default"
#define DEFAULT_PROP_DEVICE_NAME	""

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
69
70
71
72
enum
{
  PROP_0,
  PROP_DEVICE,
73
  PROP_DEVICE_NAME,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
75
};

76
77
78
79
static void gst_alsasrc_init_interfaces (GType type);

GST_BOILERPLATE_FULL (GstAlsaSrc, gst_alsasrc, GstAudioSrc,
    GST_TYPE_AUDIO_SRC, gst_alsasrc_init_interfaces);
80
81
82

GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaSrc, gst_alsasrc_mixer);

83
static void gst_alsasrc_finalize (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84
85
86
87
static void gst_alsasrc_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_alsasrc_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
Wim Taymans's avatar
Wim Taymans committed
88
89
90

static GstCaps *gst_alsasrc_getcaps (GstBaseSrc * bsrc);

91
92
93
94
static gboolean gst_alsasrc_open (GstAudioSrc * asrc);
static gboolean gst_alsasrc_prepare (GstAudioSrc * asrc,
    GstRingBufferSpec * spec);
static gboolean gst_alsasrc_unprepare (GstAudioSrc * asrc);
Wim Taymans's avatar
Wim Taymans committed
95
96
97
98
99
100
101
static gboolean gst_alsasrc_close (GstAudioSrc * asrc);
static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length);
static guint gst_alsasrc_delay (GstAudioSrc * asrc);
static void gst_alsasrc_reset (GstAudioSrc * asrc);

/* AlsaSrc signals and args */
enum
102
{
Wim Taymans's avatar
Wim Taymans committed
103
104
105
  LAST_SIGNAL
};

Wim Taymans's avatar
Wim Taymans committed
106
107
108
109
110
111
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
# define ALSA_SRC_FACTORY_ENDIANNESS   "LITTLE_ENDIAN, BIG_ENDIAN"
#else
# define ALSA_SRC_FACTORY_ENDIANNESS   "BIG_ENDIAN, LITTLE_ENDIAN"
#endif

Wim Taymans's avatar
Wim Taymans committed
112
113
114
115
116
static GstStaticPadTemplate alsasrc_src_factory =
    GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-raw-int, "
117
118
119
120
121
122
        "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, "
        "signed = (boolean) { TRUE, FALSE }, "
        "width = (int) 32, "
        "depth = (int) 32, "
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
        "audio/x-raw-int, "
Wim Taymans's avatar
Wim Taymans committed
123
        "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, "
Wim Taymans's avatar
Wim Taymans committed
124
        "signed = (boolean) { TRUE, FALSE }, "
125
126
127
128
129
130
131
132
133
134
135
136
        "width = (int) 32, "
        "depth = (int) 24, "
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
        "audio/x-raw-int, "
        "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, "
        "signed = (boolean) { TRUE, FALSE }, "
        "width = (int) 24, "
        "depth = (int) 24, "
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
        "audio/x-raw-int, "
        "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, "
        "signed = (boolean) { TRUE, FALSE }, "
Wim Taymans's avatar
Wim Taymans committed
137
138
        "width = (int) 16, "
        "depth = (int) 16, "
139
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
Wim Taymans's avatar
Wim Taymans committed
140
141
142
143
        "audio/x-raw-int, "
        "signed = (boolean) { TRUE, FALSE }, "
        "width = (int) 8, "
        "depth = (int) 8, "
144
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
Wim Taymans's avatar
Wim Taymans committed
145
146
147
    );

static void
148
gst_alsasrc_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
149
{
150
151
152
  GstAlsaSrc *src = GST_ALSA_SRC (object);

  g_free (src->device);
153
  g_mutex_free (src->alsa_lock);
154

155
  G_OBJECT_CLASS (parent_class)->finalize (object);
156
157
}

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
static gboolean
gst_alsasrc_interface_supported (GstAlsaSrc * this, GType interface_type)
{
  /* only support this one interface (wrapped by GstImplementsInterface) */
  g_assert (interface_type == GST_TYPE_MIXER);

  return gst_alsasrc_mixer_supported (this, interface_type);
}

static void
gst_implements_interface_init (GstImplementsInterfaceClass * klass)
{
  klass->supported = (gpointer) gst_alsasrc_interface_supported;
}

static void
gst_alsasrc_init_interfaces (GType type)
{
  static const GInterfaceInfo implements_iface_info = {
    (GInterfaceInitFunc) gst_implements_interface_init,
    NULL,
    NULL,
  };
  static const GInterfaceInfo mixer_iface_info = {
    (GInterfaceInitFunc) gst_alsasrc_mixer_interface_init,
    NULL,
    NULL,
  };

  g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
      &implements_iface_info);
  g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info);

  gst_alsa_type_add_device_property_probe_interface (type);
}

194
static void
Wim Taymans's avatar
Wim Taymans committed
195
gst_alsasrc_base_init (gpointer g_class)
196
197
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
198

Wim Taymans's avatar
Wim Taymans committed
199
  gst_element_class_set_details (element_class, &gst_alsasrc_details);
200

Wim Taymans's avatar
Wim Taymans committed
201
202
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&alsasrc_src_factory));
203
}
204

205
static void
Wim Taymans's avatar
Wim Taymans committed
206
gst_alsasrc_class_init (GstAlsaSrcClass * klass)
207
{
Wim Taymans's avatar
Wim Taymans committed
208
209
210
211
212
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseSrcClass *gstbasesrc_class;
  GstBaseAudioSrcClass *gstbaseaudiosrc_class;
  GstAudioSrcClass *gstaudiosrc_class;
213

Wim Taymans's avatar
Wim Taymans committed
214
215
216
217
218
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbasesrc_class = (GstBaseSrcClass *) klass;
  gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
  gstaudiosrc_class = (GstAudioSrcClass *) klass;
219

220
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_alsasrc_finalize);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
221
222
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_alsasrc_get_property);
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_alsasrc_set_property);
223

Wim Taymans's avatar
Wim Taymans committed
224
225
226
  gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps);

  gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open);
227
228
  gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasrc_prepare);
  gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_alsasrc_unprepare);
Wim Taymans's avatar
Wim Taymans committed
229
230
231
232
  gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_alsasrc_close);
  gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_alsasrc_read);
  gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_alsasrc_delay);
  gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_alsasrc_reset);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
233
234
235
236

  g_object_class_install_property (gobject_class, PROP_DEVICE,
      g_param_spec_string ("device", "Device",
          "ALSA device, as defined in an asound configuration file",
Wim Taymans's avatar
Wim Taymans committed
237
          DEFAULT_PROP_DEVICE, G_PARAM_READWRITE));
238
239
240

  g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
      g_param_spec_string ("device-name", "Device name",
Wim Taymans's avatar
Wim Taymans committed
241
242
          "Human-readable name of the sound device",
          DEFAULT_PROP_DEVICE_NAME, G_PARAM_READABLE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
243
244
245
246
247
248
249
250
251
252
253
254
}

static void
gst_alsasrc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstAlsaSrc *src;

  src = GST_ALSA_SRC (object);

  switch (prop_id) {
    case PROP_DEVICE:
255
256
      g_free (src->device);
      src->device = g_value_dup_string (value);
257
258
259
      if (src->device == NULL) {
        src->device = g_strdup (DEFAULT_PROP_DEVICE);
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_alsasrc_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstAlsaSrc *src;

  src = GST_ALSA_SRC (object);

  switch (prop_id) {
    case PROP_DEVICE:
      g_value_set_string (value, src->device);
      break;
279
    case PROP_DEVICE_NAME:
280
281
282
      g_value_take_string (value,
          gst_alsa_find_device_name (GST_OBJECT_CAST (src),
              src->device, src->handle, SND_PCM_STREAM_CAPTURE));
283
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
284
285
286
287
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
288
}
289

290
static void
291
gst_alsasrc_init (GstAlsaSrc * alsasrc, GstAlsaSrcClass * g_class)
292
{
293
  GST_DEBUG_OBJECT (alsasrc, "initializing");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
294

Wim Taymans's avatar
Wim Taymans committed
295
  alsasrc->device = g_strdup (DEFAULT_PROP_DEVICE);
296
  alsasrc->cached_caps = NULL;
297
298

  alsasrc->alsa_lock = g_mutex_new ();
Wim Taymans's avatar
Wim Taymans committed
299
}
300

Wim Taymans's avatar
Wim Taymans committed
301
#define CHECK(call, error) \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302
303
304
G_STMT_START {                  \
if ((err = call) < 0)           \
  goto error;                   \
Wim Taymans's avatar
Wim Taymans committed
305
} G_STMT_END;
306

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343

static GstCaps *
gst_alsasrc_getcaps (GstBaseSrc * bsrc)
{
  GstElementClass *element_class;
  GstPadTemplate *pad_template;
  GstAlsaSrc *src;
  GstCaps *caps;

  src = GST_ALSA_SRC (bsrc);

  if (src->handle == NULL) {
    GST_DEBUG_OBJECT (src, "device not open, using template caps");
    return NULL;                /* base class will get template caps for us */
  }

  if (src->cached_caps) {
    GST_LOG_OBJECT (src, "Returning cached caps");
    return gst_caps_ref (src->cached_caps);
  }

  element_class = GST_ELEMENT_GET_CLASS (src);
  pad_template = gst_element_class_get_pad_template (element_class, "src");
  g_return_val_if_fail (pad_template != NULL, NULL);

  caps = gst_alsa_probe_supported_formats (GST_OBJECT (src), src->handle,
      gst_pad_template_get_caps (pad_template));

  if (caps) {
    src->cached_caps = gst_caps_ref (caps);
  }

  GST_INFO_OBJECT (src, "returning caps %" GST_PTR_FORMAT, caps);

  return caps;
}

Wim Taymans's avatar
Wim Taymans committed
344
345
346
347
348
349
350
static int
set_hwparams (GstAlsaSrc * alsa)
{
  guint rrate;
  gint err, dir;
  snd_pcm_hw_params_t *params;

351
  snd_pcm_hw_params_malloc (&params);
Wim Taymans's avatar
Wim Taymans committed
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

  /* choose all parameters */
  CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config);
  /* set the interleaved read/write format */
  CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access),
      wrong_access);
  /* set the sample format */
  CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format),
      no_sample_format);
  /* set the count of channels */
  CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels),
      no_channels);
  /* set the stream rate */
  rrate = alsa->rate;
  CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, 0),
      no_rate);
  if (rrate != alsa->rate)
    goto rate_match;

  if (alsa->buffer_time != -1) {
    /* set the buffer time */
    CHECK (snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params,
            &alsa->buffer_time, &dir), buffer_time);
  }
  if (alsa->period_time != -1) {
    /* set the period time */
    CHECK (snd_pcm_hw_params_set_period_time_near (alsa->handle, params,
            &alsa->period_time, &dir), period_time);
380
381
  }

Wim Taymans's avatar
Wim Taymans committed
382
383
  /* write the parameters to device */
  CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);
384

Wim Taymans's avatar
Wim Taymans committed
385
386
  CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size),
      buffer_size);
387

Wim Taymans's avatar
Wim Taymans committed
388
389
  CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir),
      period_size);
390

391
  snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
392
  return 0;
393

Wim Taymans's avatar
Wim Taymans committed
394
395
396
  /* ERRORS */
no_config:
  {
397
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
Wim Taymans's avatar
Wim Taymans committed
398
        ("Broken configuration for recording: no configurations available: %s",
399
            snd_strerror (err)));
400
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
401
    return err;
402
  }
Wim Taymans's avatar
Wim Taymans committed
403
404
wrong_access:
  {
405
406
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Access type not available for recording: %s", snd_strerror (err)));
407
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
408
409
410
411
    return err;
  }
no_sample_format:
  {
412
413
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Sample format not available for recording: %s", snd_strerror (err)));
414
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
415
    return err;
416
  }
Wim Taymans's avatar
Wim Taymans committed
417
418
no_channels:
  {
419
420
421
422
423
424
425
426
427
428
429
430
431
    gchar *msg = NULL;

    if ((alsa->channels) == 1)
      msg = g_strdup (_("Could not open device for recording in mono mode."));
    if ((alsa->channels) == 2)
      msg = g_strdup (_("Could not open device for recording in stereo mode."));
    if ((alsa->channels) > 2)
      msg =
          g_strdup_printf (_
          ("Could not open device for recording in %d-channel mode"),
          alsa->channels);
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (msg), (snd_strerror (err)));
    g_free (msg);
432
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
433
    return err;
434
  }
Wim Taymans's avatar
Wim Taymans committed
435
436
no_rate:
  {
437
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
Wim Taymans's avatar
Wim Taymans committed
438
        ("Rate %iHz not available for recording: %s",
439
            alsa->rate, snd_strerror (err)));
440
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
441
442
443
444
    return err;
  }
rate_match:
  {
445
446
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Rate doesn't match (requested %iHz, get %iHz)", alsa->rate, err));
447
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
448
449
450
451
    return -EINVAL;
  }
buffer_time:
  {
452
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
Wim Taymans's avatar
Wim Taymans committed
453
        ("Unable to set buffer time %i for recording: %s",
454
            alsa->buffer_time, snd_strerror (err)));
455
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
456
457
458
459
    return err;
  }
buffer_size:
  {
460
461
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to get buffer size for recording: %s", snd_strerror (err)));
462
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
463
464
465
466
    return err;
  }
period_time:
  {
467
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
Wim Taymans's avatar
Wim Taymans committed
468
        ("Unable to set period time %i for recording: %s", alsa->period_time,
469
            snd_strerror (err)));
470
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
471
472
473
474
    return err;
  }
period_size:
  {
475
476
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to get period size for recording: %s", snd_strerror (err)));
477
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
478
479
480
481
    return err;
  }
set_hw_params:
  {
482
483
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set hw params for recording: %s", snd_strerror (err)));
484
    snd_pcm_hw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
485
    return err;
486
487
  }
}
Wim Taymans's avatar
Wim Taymans committed
488

489
static int
Wim Taymans's avatar
Wim Taymans committed
490
set_swparams (GstAlsaSrc * alsa)
491
{
Wim Taymans's avatar
Wim Taymans committed
492
493
494
  int err;
  snd_pcm_sw_params_t *params;

495
  snd_pcm_sw_params_malloc (&params);
Wim Taymans's avatar
Wim Taymans committed
496
497
498
499
500
501

  /* get the current swparams */
  CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config);
  /* allow the transfer when at least period_size samples can be processed */
  CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params,
          alsa->period_size), set_avail);
502
503
504
  /* start the transfer on first read */
  CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params,
          0), start_threshold);
Wim Taymans's avatar
Wim Taymans committed
505
506
507
508
509
510
  /* align all transfers to 1 sample */
  CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align);

  /* write the parameters to the recording device */
  CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params);

511
  snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
512
513
514
515
516
  return 0;

  /* ERRORS */
no_config:
  {
517
518
519
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to determine current swparams for playback: %s",
            snd_strerror (err)));
520
    snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
521
    return err;
522
  }
Wim Taymans's avatar
Wim Taymans committed
523
524
start_threshold:
  {
525
526
527
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set start threshold mode for playback: %s",
            snd_strerror (err)));
528
    snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
529
530
531
532
    return err;
  }
set_avail:
  {
533
534
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set avail min for playback: %s", snd_strerror (err)));
535
    snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
536
537
538
539
    return err;
  }
set_align:
  {
540
541
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set transfer align for playback: %s", snd_strerror (err)));
542
    snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
543
544
545
546
    return err;
  }
set_sw_params:
  {
547
548
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set sw params for playback: %s", snd_strerror (err)));
549
    snd_pcm_sw_params_free (params);
Wim Taymans's avatar
Wim Taymans committed
550
    return err;
551
552
553
  }
}

Wim Taymans's avatar
Wim Taymans committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
static gboolean
alsasrc_parse_spec (GstAlsaSrc * alsa, GstRingBufferSpec * spec)
{
  switch (spec->type) {
    case GST_BUFTYPE_LINEAR:
      alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width,
          spec->sign ? 0 : 1, spec->bigend ? 1 : 0);
      break;
    case GST_BUFTYPE_FLOAT:
      switch (spec->format) {
        case GST_FLOAT32_LE:
          alsa->format = SND_PCM_FORMAT_FLOAT_LE;
          break;
        case GST_FLOAT32_BE:
          alsa->format = SND_PCM_FORMAT_FLOAT_BE;
          break;
        case GST_FLOAT64_LE:
          alsa->format = SND_PCM_FORMAT_FLOAT64_LE;
          break;
        case GST_FLOAT64_BE:
          alsa->format = SND_PCM_FORMAT_FLOAT64_BE;
          break;
        default:
          goto error;
      }
      break;
    case GST_BUFTYPE_A_LAW:
      alsa->format = SND_PCM_FORMAT_A_LAW;
      break;
    case GST_BUFTYPE_MU_LAW:
      alsa->format = SND_PCM_FORMAT_MU_LAW;
      break;
    default:
      goto error;
588
589

  }
Wim Taymans's avatar
Wim Taymans committed
590
591
592
593
594
  alsa->rate = spec->rate;
  alsa->channels = spec->channels;
  alsa->buffer_time = spec->buffer_time;
  alsa->period_time = spec->latency_time;
  alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED;
595

Wim Taymans's avatar
Wim Taymans committed
596
597
598
599
600
601
602
  return TRUE;

  /* ERRORS */
error:
  {
    return FALSE;
  }
603
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
604

605
static gboolean
606
gst_alsasrc_open (GstAudioSrc * asrc)
607
{
Wim Taymans's avatar
Wim Taymans committed
608
609
610
611
612
613
614
615
  GstAlsaSrc *alsa;
  gint err;

  alsa = GST_ALSA_SRC (asrc);

  CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_CAPTURE,
          SND_PCM_NONBLOCK), open_error);

616
617
618
619
620
621
622
623
  if (!alsa->mixer)
    alsa->mixer = gst_alsa_mixer_new (alsa->device, GST_ALSA_MIXER_CAPTURE);

  return TRUE;

  /* ERRORS */
open_error:
  {
624
    if (err == -EBUSY) {
625
626
627
628
      GST_ELEMENT_ERROR (alsa, RESOURCE, BUSY,
          (_("Could not open audio device for recording. "
                  "Device is being used by another application.")),
          ("Device '%s' is busy", alsa->device));
629
    } else {
630
631
632
      GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
          (_("Could not open audio device for recording.")),
          ("Recording open error on device '%s': %s", alsa->device,
633
              snd_strerror (err)));
634
    }
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    return FALSE;
  }
}

static gboolean
gst_alsasrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
{
  GstAlsaSrc *alsa;
  gint err;

  alsa = GST_ALSA_SRC (asrc);

  if (!alsasrc_parse_spec (alsa, spec))
    goto spec_parse;

Wim Taymans's avatar
Wim Taymans committed
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
  CHECK (snd_pcm_nonblock (alsa->handle, 0), non_block);

  CHECK (set_hwparams (alsa), hw_params_failed);
  CHECK (set_swparams (alsa), sw_params_failed);
  CHECK (snd_pcm_prepare (alsa->handle), prepare_failed);

  alsa->bytes_per_sample = spec->bytes_per_sample;
  spec->segsize = alsa->period_size * spec->bytes_per_sample;
  spec->segtotal = alsa->buffer_size / alsa->period_size;
  spec->silence_sample[0] = 0;
  spec->silence_sample[1] = 0;
  spec->silence_sample[2] = 0;
  spec->silence_sample[3] = 0;

  return TRUE;

  /* ERRORS */
spec_parse:
  {
669
670
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Error parsing spec"));
Wim Taymans's avatar
Wim Taymans committed
671
672
673
674
    return FALSE;
  }
non_block:
  {
675
676
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Could not set device to blocking: %s", snd_strerror (err)));
Wim Taymans's avatar
Wim Taymans committed
677
678
679
680
    return FALSE;
  }
hw_params_failed:
  {
681
682
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Setting of hwparams failed: %s", snd_strerror (err)));
Wim Taymans's avatar
Wim Taymans committed
683
684
685
686
    return FALSE;
  }
sw_params_failed:
  {
687
688
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Setting of swparams failed: %s", snd_strerror (err)));
Wim Taymans's avatar
Wim Taymans committed
689
690
691
692
    return FALSE;
  }
prepare_failed:
  {
693
694
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Prepare failed: %s", snd_strerror (err)));
Wim Taymans's avatar
Wim Taymans committed
695
696
697
    return FALSE;
  }
}
698

Wim Taymans's avatar
Wim Taymans committed
699
static gboolean
700
gst_alsasrc_unprepare (GstAudioSrc * asrc)
Wim Taymans's avatar
Wim Taymans committed
701
702
{
  GstAlsaSrc *alsa;
703
  gint err;
704

Wim Taymans's avatar
Wim Taymans committed
705
  alsa = GST_ALSA_SRC (asrc);
706

707
708
709
710
711
  CHECK (snd_pcm_drop (alsa->handle), drop);

  CHECK (snd_pcm_hw_free (alsa->handle), hw_free);

  CHECK (snd_pcm_nonblock (alsa->handle, 1), non_block);
712

Wim Taymans's avatar
Wim Taymans committed
713
  return TRUE;
714
715
716
717

  /* ERRORS */
drop:
  {
718
719
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Could not drop samples: %s", snd_strerror (err)));
720
721
722
723
    return FALSE;
  }
hw_free:
  {
724
725
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Could not free hw params: %s", snd_strerror (err)));
726
727
728
729
    return FALSE;
  }
non_block:
  {
730
731
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Could not set device to nonblocking: %s", snd_strerror (err)));
732
733
    return FALSE;
  }
734
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
735

736
737
738
739
740
741
742
743
744
745
746
747
static gboolean
gst_alsasrc_close (GstAudioSrc * asrc)
{
  GstAlsaSrc *alsa = GST_ALSA_SRC (asrc);

  snd_pcm_close (alsa->handle);

  if (alsa->mixer) {
    gst_alsa_mixer_free (alsa->mixer);
    alsa->mixer = NULL;
  }

748
749
  gst_caps_replace (&alsa->cached_caps, NULL);

750
751
  return TRUE;
}
Wim Taymans's avatar
Wim Taymans committed
752
753
754
755
756

/*
 *   Underrun and suspend recovery
 */
static gint
757
xrun_recovery (GstAlsaSrc * alsa, snd_pcm_t * handle, gint err)
758
{
759
  GST_DEBUG_OBJECT (alsa, "xrun recovery %d", err);
Wim Taymans's avatar
Wim Taymans committed
760
761
762
763

  if (err == -EPIPE) {          /* under-run */
    err = snd_pcm_prepare (handle);
    if (err < 0)
764
765
      GST_WARNING_OBJECT (alsa,
          "Can't recovery from underrun, prepare failed: %s",
Wim Taymans's avatar
Wim Taymans committed
766
767
768
769
770
771
772
773
774
          snd_strerror (err));
    return 0;
  } else if (err == -ESTRPIPE) {
    while ((err = snd_pcm_resume (handle)) == -EAGAIN)
      g_usleep (100);           /* wait until the suspend flag is released */

    if (err < 0) {
      err = snd_pcm_prepare (handle);
      if (err < 0)
775
776
        GST_WARNING_OBJECT (alsa,
            "Can't recovery from suspend, prepare failed: %s",
Wim Taymans's avatar
Wim Taymans committed
777
            snd_strerror (err));
778
    }
Wim Taymans's avatar
Wim Taymans committed
779
    return 0;
780
  }
Wim Taymans's avatar
Wim Taymans committed
781
  return err;
782
783
}

Wim Taymans's avatar
Wim Taymans committed
784
785
static guint
gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length)
786
{
Wim Taymans's avatar
Wim Taymans committed
787
788
789
790
791
792
793
794
795
796
  GstAlsaSrc *alsa;
  gint err;
  gint cptr;
  gint16 *ptr;

  alsa = GST_ALSA_SRC (asrc);

  cptr = length / alsa->bytes_per_sample;
  ptr = data;

797
  GST_ALSA_SRC_LOCK (asrc);
Wim Taymans's avatar
Wim Taymans committed
798
799
800
  while (cptr > 0) {
    if ((err = snd_pcm_readi (alsa->handle, ptr, cptr)) < 0) {
      if (err == -EAGAIN) {
801
        GST_DEBUG_OBJECT (asrc, "Read error: %s", snd_strerror (err));
Wim Taymans's avatar
Wim Taymans committed
802
        continue;
803
      } else if (xrun_recovery (alsa, alsa->handle, err) < 0) {
Wim Taymans's avatar
Wim Taymans committed
804
805
806
        goto read_error;
      }
      continue;
807
808
    }

Wim Taymans's avatar
Wim Taymans committed
809
810
    ptr += err * alsa->channels;
    cptr -= err;
811
  }
812
813
  GST_ALSA_SRC_UNLOCK (asrc);

Wim Taymans's avatar
Wim Taymans committed
814
  return length - cptr;
815

Wim Taymans's avatar
Wim Taymans committed
816
read_error:
817
  {
818
    GST_ALSA_SRC_UNLOCK (asrc);
Wim Taymans's avatar
Wim Taymans committed
819
820
821
    return length;              /* skip one period */
  }
}
822

Wim Taymans's avatar
Wim Taymans committed
823
824
825
826
827
static guint
gst_alsasrc_delay (GstAudioSrc * asrc)
{
  GstAlsaSrc *alsa;
  snd_pcm_sframes_t delay;
828
  int res;
829

Wim Taymans's avatar
Wim Taymans committed
830
  alsa = GST_ALSA_SRC (asrc);
831

832
833
834
835
836
  res = snd_pcm_delay (alsa->handle, &delay);
  if (G_UNLIKELY (res < 0)) {
    GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res);
    delay = 0;
  }
837

838
  return CLAMP (delay, 0, alsa->buffer_size);
839
840
841
}

static void
Wim Taymans's avatar
Wim Taymans committed
842
gst_alsasrc_reset (GstAudioSrc * asrc)
843
{
Wim Taymans's avatar
Wim Taymans committed
844
845
  GstAlsaSrc *alsa;
  gint err;
846

Wim Taymans's avatar
Wim Taymans committed
847
  alsa = GST_ALSA_SRC (asrc);
848

849
850
  GST_ALSA_SRC_LOCK (asrc);
  GST_DEBUG_OBJECT (alsa, "drop");
Wim Taymans's avatar
Wim Taymans committed
851
  CHECK (snd_pcm_drop (alsa->handle), drop_error);
852
  GST_DEBUG_OBJECT (alsa, "prepare");
Wim Taymans's avatar
Wim Taymans committed
853
  CHECK (snd_pcm_prepare (alsa->handle), prepare_error);
854
855
  GST_DEBUG_OBJECT (alsa, "reset done");
  GST_ALSA_SRC_UNLOCK (asrc);
856

Wim Taymans's avatar
Wim Taymans committed
857
  return;
858

Wim Taymans's avatar
Wim Taymans committed
859
860
861
  /* ERRORS */
drop_error:
  {
862
863
864
    GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s",
        snd_strerror (err));
    GST_ALSA_SRC_UNLOCK (asrc);
Wim Taymans's avatar
Wim Taymans committed
865
866
867
868
    return;
  }
prepare_error:
  {
869
870
871
    GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s",
        snd_strerror (err));
    GST_ALSA_SRC_UNLOCK (asrc);
Wim Taymans's avatar
Wim Taymans committed
872
873
    return;
  }
874
}