gstosssrc.c 13.5 KB
Newer Older
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1 2
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3
 *               2000,2005 Wim Taymans <wim@fluendo.com>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * gstosssrc.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.
 */

23 24 25 26
/**
 * SECTION:element-osssrc
 *
 * This element lets you record sound using the Open Sound System (OSS).
27 28
 *
 * <refsect2>
29
 * <title>Example pipelines</title>
30
 * |[
31
 * gst-launch -v osssrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=mymusic.ogg
32
 * ]| will record sound from your sound card using OSS and encode it to an
33 34 35 36 37
 * Ogg/Vorbis file (this will only work if your mixer settings are right
 * and the right inputs enabled etc.)
 * </refsect2>
 */

38 39 40
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
41

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
42
#include <sys/ioctl.h>
43
#include <fcntl.h>
Wim Taymans's avatar
Wim Taymans committed
44
#include <errno.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
45
#include <unistd.h>
Wim Taymans's avatar
Wim Taymans committed
46
#include <string.h>
47 48 49 50 51 52 53 54 55 56 57 58 59 60

#ifdef HAVE_OSS_INCLUDE_IN_SYS
# include <sys/soundcard.h>
#else
# ifdef HAVE_OSS_INCLUDE_IN_ROOT
#  include <soundcard.h>
# else
#  ifdef HAVE_OSS_INCLUDE_IN_MACHINE
#   include <machine/soundcard.h>
#  else
#   error "What to include?"
#  endif /* HAVE_OSS_INCLUDE_IN_MACHINE */
# endif /* HAVE_OSS_INCLUDE_IN_ROOT */
#endif /* HAVE_OSS_INCLUDE_IN_SYS */
61

62
#include "gstosssrc.h"
63
#include "common.h"
64

65 66
#include <gst/gst-i18n-plugin.h>

67 68
GST_DEBUG_CATEGORY_EXTERN (oss_debug);
#define GST_CAT_DEFAULT oss_debug
69

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
70 71
#define DEFAULT_DEVICE          "/dev/dsp"
#define DEFAULT_DEVICE_NAME     ""
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
72

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73 74
enum
{
75 76 77
  PROP_0,
  PROP_DEVICE,
  PROP_DEVICE_NAME,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
78 79
};

80 81
GST_BOILERPLATE_WITH_INTERFACE (GstOssSrc, gst_oss_src, GstAudioSrc,
    GST_TYPE_AUDIO_SRC, GstMixer, GST_TYPE_MIXER, gst_oss_src_mixer);
82 83 84 85 86 87 88 89 90

GST_IMPLEMENT_OSS_MIXER_METHODS (GstOssSrc, gst_oss_src_mixer);

static void gst_oss_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
static void gst_oss_src_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);

static void gst_oss_src_dispose (GObject * object);
91
static void gst_oss_src_finalize (GstOssSrc * osssrc);
92 93 94 95 96 97 98 99 100 101 102 103 104

static GstCaps *gst_oss_src_getcaps (GstBaseSrc * bsrc);

static gboolean gst_oss_src_open (GstAudioSrc * asrc);
static gboolean gst_oss_src_close (GstAudioSrc * asrc);
static gboolean gst_oss_src_prepare (GstAudioSrc * asrc,
    GstRingBufferSpec * spec);
static gboolean gst_oss_src_unprepare (GstAudioSrc * asrc);
static guint gst_oss_src_read (GstAudioSrc * asrc, gpointer data, guint length);
static guint gst_oss_src_delay (GstAudioSrc * asrc);
static void gst_oss_src_reset (GstAudioSrc * asrc);


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106 107 108 109
static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-raw-int, "
110
        "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
111
        "signed = (boolean) { TRUE, FALSE }, "
112
        "width = (int) 16, "
113
        "depth = (int) 16, "
114 115 116 117 118
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
        "audio/x-raw-int, "
        "signed = (boolean) { TRUE, FALSE }, "
        "width = (int) 8, "
        "depth = (int) 8, "
119
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
120 121
    );

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122

123 124
static void
gst_oss_src_dispose (GObject * object)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
{
126
  G_OBJECT_CLASS (parent_class)->dispose (object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
127 128
}

129
static void
130
gst_oss_src_base_init (gpointer g_class)
131 132
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133

134 135 136 137
  gst_element_class_set_details_simple (element_class, "Audio Source (OSS)",
      "Source/Audio",
      "Capture from a sound card via OSS",
      "Erik Walthinsen <omega@cse.ogi.edu>, " "Wim Taymans <wim@fluendo.com>");
138

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
139 140
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&osssrc_src_factory));
141
}
142

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143
static void
144
gst_oss_src_class_init (GstOssSrcClass * klass)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145 146
{
  GObjectClass *gobject_class;
147 148
  GstBaseSrcClass *gstbasesrc_class;
  GstAudioSrcClass *gstaudiosrc_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
150
  gobject_class = (GObjectClass *) klass;
151 152 153
  gstbasesrc_class = (GstBaseSrcClass *) klass;
  gstaudiosrc_class = (GstAudioSrcClass *) klass;

154 155 156 157
  gobject_class->dispose = gst_oss_src_dispose;
  gobject_class->finalize = (GObjectFinalizeFunc) gst_oss_src_finalize;
  gobject_class->get_property = gst_oss_src_get_property;
  gobject_class->set_property = gst_oss_src_set_property;
158 159 160 161 162 163 164 165 166 167 168 169 170

  gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss_src_getcaps);

  gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_oss_src_open);
  gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_oss_src_prepare);
  gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss_src_unprepare);
  gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_oss_src_close);
  gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_oss_src_read);
  gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_oss_src_delay);
  gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_oss_src_reset);

  g_object_class_install_property (gobject_class, PROP_DEVICE,
      g_param_spec_string ("device", "Device",
171
          "OSS device (usually /dev/dspN)", DEFAULT_DEVICE, G_PARAM_READWRITE));
172 173 174

  g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
      g_param_spec_string ("device-name", "Device name",
175 176
          "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
          G_PARAM_READABLE));
177
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178

179 180 181 182 183
static void
gst_oss_src_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstOssSrc *src;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184

185
  src = GST_OSS_SRC (object);
186

187 188 189 190
  switch (prop_id) {
    case PROP_DEVICE:
      if (src->device)
        g_free (src->device);
191
      src->device = g_value_dup_string (value);
192 193 194 195 196
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
197 198
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
199
static void
200 201
gst_oss_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
202
{
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
  GstOssSrc *src;

  src = GST_OSS_SRC (object);

  switch (prop_id) {
    case PROP_DEVICE:
      g_value_set_string (value, src->device);
      break;
    case PROP_DEVICE_NAME:
      g_value_set_string (value, src->device_name);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
218
}
219

220
static void
221
gst_oss_src_init (GstOssSrc * osssrc, GstOssSrcClass * g_class)
222
{
223 224
  const gchar *device;

225
  GST_DEBUG ("initializing osssrc");
226

227 228 229 230
  device = g_getenv ("AUDIODEV");
  if (device == NULL)
    device = DEFAULT_DEVICE;

231
  osssrc->fd = -1;
232
  osssrc->device = g_strdup (device);
233
  osssrc->device_name = g_strdup (DEFAULT_DEVICE_NAME);
234
  osssrc->probed_caps = NULL;
235
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
236

237 238 239 240 241 242 243 244 245
static void
gst_oss_src_finalize (GstOssSrc * osssrc)
{
  g_free (osssrc->device);
  g_free (osssrc->device_name);

  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (osssrc));
}

246
static GstCaps *
247
gst_oss_src_getcaps (GstBaseSrc * bsrc)
248
{
249
  GstOssSrc *osssrc;
250 251
  GstCaps *caps;

252
  osssrc = GST_OSS_SRC (bsrc);
253

254
  if (osssrc->fd == -1) {
255 256
    GST_DEBUG_OBJECT (osssrc, "device not open, using template caps");
    return NULL;                /* base class will get template caps for us */
257 258
  }

259 260 261 262 263 264 265 266 267 268 269 270 271
  if (osssrc->probed_caps) {
    GST_LOG_OBJECT (osssrc, "Returning cached caps");
    return gst_caps_ref (osssrc->probed_caps);
  }

  caps = gst_oss_helper_probe_caps (osssrc->fd);

  if (caps) {
    osssrc->probed_caps = gst_caps_ref (caps);
  }

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

272 273 274
  return caps;
}

275 276
static gint
ilog2 (gint x)
277
{
278 279 280 281 282 283 284 285 286 287 288 289
  /* well... hacker's delight explains... */
  x = x | (x >> 1);
  x = x | (x >> 2);
  x = x | (x >> 4);
  x = x | (x >> 8);
  x = x | (x >> 16);
  x = x - ((x >> 1) & 0x55555555);
  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
  x = (x + (x >> 4)) & 0x0f0f0f0f;
  x = x + (x >> 8);
  x = x + (x >> 16);
  return (x & 0x0000003f) - 1;
290 291
}

292 293
static gint
gst_oss_src_get_format (GstBufferFormat fmt)
294
{
295
  gint result;
296

297 298 299 300 301 302 303 304 305 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
  switch (fmt) {
    case GST_MU_LAW:
      result = AFMT_MU_LAW;
      break;
    case GST_A_LAW:
      result = AFMT_A_LAW;
      break;
    case GST_IMA_ADPCM:
      result = AFMT_IMA_ADPCM;
      break;
    case GST_U8:
      result = AFMT_U8;
      break;
    case GST_S16_LE:
      result = AFMT_S16_LE;
      break;
    case GST_S16_BE:
      result = AFMT_S16_BE;
      break;
    case GST_S8:
      result = AFMT_S8;
      break;
    case GST_U16_LE:
      result = AFMT_U16_LE;
      break;
    case GST_U16_BE:
      result = AFMT_U16_BE;
      break;
    case GST_MPEG:
      result = AFMT_MPEG;
      break;
    default:
      result = 0;
      break;
  }
  return result;
333
}
334

335 336
static gboolean
gst_oss_src_open (GstAudioSrc * asrc)
337
{
338 339
  GstOssSrc *oss;
  int mode;
340

341
  oss = GST_OSS_SRC (asrc);
342

343 344
  mode = O_RDONLY;
  mode |= O_NONBLOCK;
345

346
  oss->fd = open (oss->device, mode, 0);
347 348 349 350 351 352 353 354
  if (oss->fd == -1) {
    switch (errno) {
      case EACCES:
        goto no_permission;
      default:
        goto open_failed;
    }
  }
355

356
  if (!oss->mixer) {
357 358
    oss->mixer = gst_ossmixer_new ("/dev/mixer", GST_OSS_MIXER_CAPTURE);

359
    if (oss->mixer) {
360
      g_free (oss->device_name);
361 362 363
      oss->device_name = g_strdup (oss->mixer->cardname);
    }
  }
364
  return TRUE;
365

366 367 368
no_permission:
  {
    GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
369
        (_("Could not open audio device for recording. "
370 371 372 373
                "You don't have permission to open the device.")),
        GST_ERROR_SYSTEM);
    return FALSE;
  }
374 375 376
open_failed:
  {
    GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
377
        (_("Could not open audio device for recording.")),
378
        ("Unable to open device %s for recording: %s",
379
            oss->device, g_strerror (errno)));
380 381
    return FALSE;
  }
382 383
}

384 385
static gboolean
gst_oss_src_close (GstAudioSrc * asrc)
386
{
387 388 389 390 391 392 393 394 395 396 397
  GstOssSrc *oss;

  oss = GST_OSS_SRC (asrc);

  close (oss->fd);

  if (oss->mixer) {
    gst_ossmixer_free (oss->mixer);
    oss->mixer = NULL;
  }

398 399
  gst_caps_replace (&oss->probed_caps, NULL);

400
  return TRUE;
401
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
402

403 404
static gboolean
gst_oss_src_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
405
{
406 407 408
  GstOssSrc *oss;
  struct audio_buf_info info;
  int mode;
409
  int fmt, tmp;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410

411
  oss = GST_OSS_SRC (asrc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412

413 414
  mode = fcntl (oss->fd, F_GETFL);
  mode &= ~O_NONBLOCK;
415 416
  if (fcntl (oss->fd, F_SETFL, mode) == -1)
    goto non_block;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417

418 419
  fmt = gst_oss_src_get_format (spec->format);
  if (fmt == 0)
420
    goto wrong_format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421

422 423
  tmp = ilog2 (spec->segsize);
  tmp = ((spec->segtotal & 0x7fff) << 16) | tmp;
424 425
  GST_DEBUG_OBJECT (oss, "set segsize: %d, segtotal: %d, value: %08x",
      spec->segsize, spec->segtotal, tmp);
426

427
  SET_PARAM (oss, SNDCTL_DSP_SETFRAGMENT, tmp, "SETFRAGMENT");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
428

429
  SET_PARAM (oss, SNDCTL_DSP_RESET, 0, "RESET");
430

431
  SET_PARAM (oss, SNDCTL_DSP_SETFMT, fmt, "SETFMT");
432
  if (spec->channels == 2)
433 434 435
    SET_PARAM (oss, SNDCTL_DSP_STEREO, 1, "STEREO");
  SET_PARAM (oss, SNDCTL_DSP_CHANNELS, spec->channels, "CHANNELS");
  SET_PARAM (oss, SNDCTL_DSP_SPEED, spec->rate, "SPEED");
436

437
  GET_PARAM (oss, SNDCTL_DSP_GETISPACE, &info, "GETISPACE");
438

439 440
  spec->segsize = info.fragsize;
  spec->segtotal = info.fragstotal;
441 442 443 444 445 446

  if (spec->width != 16 && spec->width != 8)
    goto dodgy_width;

  spec->bytes_per_sample = (spec->width / 8) * spec->channels;
  oss->bytes_per_sample = (spec->width / 8) * spec->channels;
447
  memset (spec->silence_sample, 0, spec->bytes_per_sample);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448

449 450
  GST_DEBUG_OBJECT (oss, "got segsize: %d, segtotal: %d, value: %08x",
      spec->segsize, spec->segtotal, tmp);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
451

452
  return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453

454 455 456 457 458 459 460
non_block:
  {
    GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
        ("Unable to set device %s in non blocking mode: %s",
            oss->device, g_strerror (errno)), (NULL));
    return FALSE;
  }
461 462
wrong_format:
  {
463 464
    GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
        ("Unable to get format %d", spec->format), (NULL));
465
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466
  }
467 468 469 470 471 472
dodgy_width:
  {
    GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
        ("Unexpected width %d", spec->width), (NULL));
    return FALSE;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473 474
}

475 476
static gboolean
gst_oss_src_unprepare (GstAudioSrc * asrc)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
477
{
478
  /* could do a SNDCTL_DSP_RESET, but the OSS manual recommends a close/open */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
479

480 481
  if (!gst_oss_src_close (asrc))
    goto couldnt_close;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482

483 484
  if (!gst_oss_src_open (asrc))
    goto couldnt_reopen;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
485

486
  return TRUE;
487

488 489
couldnt_close:
  {
490
    GST_DEBUG_OBJECT (asrc, "Could not close the audio device");
491 492 493 494
    return FALSE;
  }
couldnt_reopen:
  {
495
    GST_DEBUG_OBJECT (asrc, "Could not reopen the audio device");
496
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
497
  }
498 499
}

500 501
static guint
gst_oss_src_read (GstAudioSrc * asrc, gpointer data, guint length)
502
{
503
  return read (GST_OSS_SRC (asrc)->fd, data, length);
504 505
}

506 507
static guint
gst_oss_src_delay (GstAudioSrc * asrc)
508
{
509 510 511
  GstOssSrc *oss;
  gint delay = 0;
  gint ret;
512

513
  oss = GST_OSS_SRC (asrc);
514

515 516 517 518 519 520 521
#ifdef SNDCTL_DSP_GETODELAY
  ret = ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay);
#else
  ret = -1;
#endif
  if (ret < 0) {
    audio_buf_info info;
522

523
    ret = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &info);
524

525
    delay = (ret < 0 ? 0 : (info.fragstotal * info.fragsize) - info.bytes);
526
  }
527
  return delay / oss->bytes_per_sample;
528 529
}

530 531
static void
gst_oss_src_reset (GstAudioSrc * asrc)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
532
{
533 534 535
  /* There's nothing we can do here really: OSS can't handle access to the
   * same device/fd from multiple threads and might deadlock or blow up in
   * other ways if we try an ioctl SNDCTL_DSP_RESET or similar */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
536
}