change NULL to (NULL) for GST_ELEMENT_ERROR
[platform/upstream/gst-plugins-good.git] / sys / oss / gstosselement.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gstosssink.c: 
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "gst/gst-i18n-plugin.h"
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioctl.h>
31 #include <fcntl.h>
32 #include <sys/soundcard.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <string.h>
36
37 #include <gst/propertyprobe/propertyprobe.h>
38
39 #include "gstosselement.h"
40 #include "gstossmixer.h"
41
42 enum {
43   ARG_0,
44   ARG_DEVICE,
45   ARG_MIXERDEV,
46   ARG_DEVICE_NAME,
47 };
48
49 /* elementfactory information */
50 static GstElementDetails gst_osselement_details = GST_ELEMENT_DETAILS (
51   "Audio Mixer (OSS)",
52   "Generic/Audio",
53   "OSS-based mixer element",
54   "Ronald Bultje <rbultje@ronald.bitfreak.net>"
55 );
56
57 static void                     gst_osselement_base_init        (GstOssElementClass *klass);
58 static void                     gst_osselement_class_init       (GstOssElementClass *klass);
59
60 static void                     gst_ossprobe_interface_init     (GstPropertyProbeInterface *iface);
61 static void                     gst_osselement_init             (GstOssElement *oss);
62 static void                     gst_osselement_dispose          (GObject *object);
63
64 static void                     gst_osselement_set_property     (GObject *object,
65                                                                  guint prop_id, 
66                                                                  const GValue *value,
67                                                                  GParamSpec *pspec);
68 static void                     gst_osselement_get_property     (GObject *object,
69                                                                  guint prop_id,
70                                                                  GValue *value,
71                                                                  GParamSpec *pspec);
72 static GstElementStateReturn    gst_osselement_change_state     (GstElement *element);
73
74 static GstElementClass *parent_class = NULL;
75 /*static guint gst_osssrc_signals[LAST_SIGNAL] = { 0 }; */
76
77 GType
78 gst_osselement_get_type (void) 
79 {
80   static GType osselement_type = 0;
81
82   if (!osselement_type) {
83     static const GTypeInfo osselement_info = {
84       sizeof(GstOssElementClass),
85       (GBaseInitFunc)gst_osselement_base_init,
86       NULL,
87       (GClassInitFunc)gst_osselement_class_init,
88       NULL,
89       NULL,
90       sizeof(GstOssElement),
91       0,
92       (GInstanceInitFunc)gst_osselement_init
93     };
94     static const GInterfaceInfo ossiface_info = {
95       (GInterfaceInitFunc) gst_oss_interface_init,
96       NULL,
97       NULL
98     };
99     static const GInterfaceInfo ossmixer_info = {
100       (GInterfaceInitFunc) gst_ossmixer_interface_init,
101       NULL,
102       NULL
103     };
104     static const GInterfaceInfo ossprobe_info = {
105       (GInterfaceInitFunc) gst_ossprobe_interface_init,
106       NULL,
107       NULL
108     };
109
110     osselement_type = g_type_register_static (GST_TYPE_ELEMENT,
111                                               "GstOssElement",
112                                               &osselement_info, 0);
113     g_type_add_interface_static (osselement_type,
114                                  GST_TYPE_IMPLEMENTS_INTERFACE,
115                                  &ossiface_info);
116     g_type_add_interface_static (osselement_type,
117                                  GST_TYPE_MIXER,
118                                  &ossmixer_info);
119     g_type_add_interface_static (osselement_type,
120                                  GST_TYPE_PROPERTY_PROBE,
121                                  &ossprobe_info);
122   }
123
124   return osselement_type;
125 }
126
127 static void
128 gst_osselement_base_init (GstOssElementClass *klass)
129 {
130   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
131
132   klass->device_combinations = NULL;
133
134   gst_element_class_set_details (element_class, &gst_osselement_details);
135 }
136
137 static void
138 gst_osselement_class_init (GstOssElementClass *klass) 
139 {
140   GObjectClass *gobject_class;
141   GstElementClass *gstelement_class;
142
143   gobject_class = (GObjectClass*)klass;
144   gstelement_class = (GstElementClass*)klass;
145
146   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
147
148   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE,
149     g_param_spec_string ("device", "Device", "OSS device (/dev/dspN usually)",
150                          "default", G_PARAM_READWRITE));
151   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MIXERDEV,
152     g_param_spec_string ("mixerdev", "Mixer device",
153                          "OSS mixer device (/dev/mixerN usually)",
154                          "default", G_PARAM_READWRITE));
155   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE_NAME,
156     g_param_spec_string ("device_name", "Device name", "Name of the device",
157                          NULL, G_PARAM_READABLE));
158   
159   gobject_class->set_property = gst_osselement_set_property;
160   gobject_class->get_property = gst_osselement_get_property;
161   gobject_class->dispose      = gst_osselement_dispose;
162
163   gstelement_class->change_state = gst_osselement_change_state;
164 }
165
166 static const GList *
167 gst_ossprobe_get_properties (GstPropertyProbe *probe)
168 {
169   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
170   static GList *list = NULL;
171
172   if (!list) {
173     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
174   }
175
176   return list;
177 }
178
179 static void
180 gst_osselement_probe (gchar  *device_base,
181                       gint    device_num,
182                       gchar **put)
183 {
184   gchar *device;
185   struct stat s;
186
187   /* only if yet unfilled */
188   if (*put != NULL)
189     return;
190
191   if (device_num == 0)
192     device = g_strdup (device_base);
193   else
194     device = g_strdup_printf ("%s%d", device_base, device_num);
195
196   if (lstat (device, &s) || !S_ISCHR (s.st_mode))
197     goto end;
198
199   *put = device;
200   return;
201
202 end:
203   g_free (device);
204 }
205
206 static gboolean
207 gst_osselement_class_probe_devices (GstOssElementClass *klass,
208                                     gboolean            check)
209 {
210   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
211   static gboolean init = FALSE;
212   static GList *device_combinations;
213   GList *padtempllist;
214   gint openmode = O_RDONLY;
215   gboolean mixer = FALSE;
216
217   /* Ok, so how do we open the device? We assume that we have (max.) one
218    * pad, and if this is a sinkpad, we're osssink (w). else, we're osssrc
219    * (r) */
220   padtempllist = gst_element_class_get_pad_template_list (eklass);
221   if (padtempllist != NULL) {
222     GstPadTemplate *firstpadtempl = padtempllist->data;
223     if (GST_PAD_TEMPLATE_DIRECTION (firstpadtempl) == GST_PAD_SINK) {
224       openmode = O_WRONLY;
225     }
226     mixer = TRUE;
227   }
228
229   if (!init && !check) {
230     gchar *dsp_base[] = { "/dev/dsp", "/dev/sound/dsp", NULL };
231     gchar *mixer_base[] = { "/dev/mixer", "/dev/sound/mixer", NULL };
232     GstOssDeviceCombination devices[16];
233     gint n;
234
235     while (device_combinations) {
236       GList *item = device_combinations;
237       GstOssDeviceCombination *combi = item->data;
238
239       device_combinations = g_list_remove (device_combinations, item);
240
241       g_free (combi->dsp);
242       g_free (combi->mixer);
243       g_free (combi);
244     }
245
246     /* probe for all /dev entries */
247     memset (devices, 0, sizeof (devices));
248
249     /* OSS (without devfs) allows at max. 16 devices */
250     for (n = 0; n < 16; n++) {
251       gint base;
252
253       for (base = 0; dsp_base[base] != NULL; base++)
254         gst_osselement_probe (dsp_base[base], n, &devices[n].dsp);
255
256       for (base = 0; mixer_base[base] != NULL; base++)
257         gst_osselement_probe (mixer_base[base], n, &devices[n].mixer);
258     }
259
260     /* does the device exist (can we open them)? */
261     for (n = 0; n < 16; n++) {
262       gint fd;
263
264       if (!devices[n].dsp)
265         continue;
266
267       /* we just check the dsp. we assume the mixer always works.
268        * we don't need a mixer anyway (says OSS)... If we are a
269        * mixer element, we use the mixer anyway. */
270       if ((fd = open (mixer ? devices[n].mixer :
271                          devices[n].dsp, openmode)) > 0 || errno == EBUSY) {
272         GstOssDeviceCombination *combi;
273
274         if (fd > 0)
275           close (fd);
276
277         /* yay! \o/ */
278         combi = g_new0 (GstOssDeviceCombination, 1);
279         combi->dsp   = devices[n].dsp;
280         combi->mixer = devices[n].mixer;
281         devices[n].dsp = devices[n].mixer = NULL;
282
283         device_combinations = g_list_append (device_combinations, combi);
284       }
285     }
286
287     /* free */
288     for (n = 0; n < 16; n++) {
289       if (devices[n].dsp)
290         g_free (devices[n].dsp);
291
292       if (devices[n].mixer)
293         g_free (devices[n].mixer);
294     }
295
296     init = TRUE;
297   }
298
299   klass->device_combinations = device_combinations;
300
301   return init;
302 }
303
304 static GValueArray *
305 gst_osselement_class_list_devices (GstOssElementClass *klass)
306 {
307   GValueArray *array;
308   GValue value = { 0 };
309   GList *item;
310
311   if (!klass->device_combinations)
312     return NULL;
313
314   array = g_value_array_new (g_list_length (klass->device_combinations));
315   item = klass->device_combinations;
316   g_value_init (&value, G_TYPE_STRING);
317   while (item) {
318     GstOssDeviceCombination *combi = item->data;
319
320     g_value_set_string (&value, combi->dsp);
321     g_value_array_append (array, &value);
322
323     item = item->next;
324   }
325   g_value_unset (&value);
326
327   return array;
328 }
329
330 static void
331 gst_ossprobe_probe_property (GstPropertyProbe *probe,
332                              guint             prop_id,
333                              const GParamSpec *pspec)
334 {
335   GstOssElementClass *klass = GST_OSSELEMENT_GET_CLASS (probe);
336
337   switch (prop_id) {
338     case ARG_DEVICE:
339       gst_osselement_class_probe_devices (klass, FALSE);
340       break;
341     default:
342       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
343       break;
344   }
345 }
346
347 static gboolean
348 gst_ossprobe_needs_probe (GstPropertyProbe *probe,
349                           guint             prop_id,
350                           const GParamSpec *pspec)
351 {
352   GstOssElementClass *klass = GST_OSSELEMENT_GET_CLASS (probe);
353   gboolean ret = FALSE;
354
355   switch (prop_id) {
356     case ARG_DEVICE:
357       ret = !gst_osselement_class_probe_devices (klass, TRUE);
358       break;
359     default:
360       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
361       break;
362   }
363
364   return ret;
365 }
366
367 static GValueArray *
368 gst_ossprobe_get_values (GstPropertyProbe *probe,
369                          guint             prop_id,
370                          const GParamSpec *pspec)
371 {
372   GstOssElementClass *klass = GST_OSSELEMENT_GET_CLASS (probe);
373   GValueArray *array = NULL;
374
375   switch (prop_id) {
376     case ARG_DEVICE:
377       array = gst_osselement_class_list_devices (klass);
378       break;
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
381       break;
382   }
383
384   return array;
385 }
386
387 static void
388 gst_ossprobe_interface_init (GstPropertyProbeInterface *iface)
389 {
390   iface->get_properties = gst_ossprobe_get_properties;
391   iface->probe_property = gst_ossprobe_probe_property;
392   iface->needs_probe    = gst_ossprobe_needs_probe;
393   iface->get_values     = gst_ossprobe_get_values;
394 }
395
396 static void 
397 gst_osselement_init (GstOssElement *oss) 
398 {
399   oss->device = g_strdup ("/dev/dsp");
400   oss->mixer_dev = g_strdup ("/dev/mixer");
401   oss->fd = -1;
402   oss->mixer_fd = -1;
403   oss->tracklist = NULL;
404   oss->device_name = NULL;
405
406   gst_osselement_reset (oss);
407 }
408
409 static void
410 gst_osselement_dispose (GObject *object)
411 {
412   GstOssElement *oss = (GstOssElement *) object;
413
414   g_free (oss->device);
415   g_free (oss->mixer_dev);
416
417   G_OBJECT_CLASS (parent_class)->dispose (object);
418 }
419
420 void 
421 gst_osselement_reset (GstOssElement *oss) 
422 {
423   oss->law = 0;
424   oss->endianness = G_BYTE_ORDER;
425   oss->sign = TRUE;
426   oss->width = 16;
427   oss->depth = 16;
428   oss->channels = 2;
429   oss->rate = 44100;
430   oss->fragment = 6;
431   oss->bps = 0;
432
433 /* AFMT_*_BE not available on all OSS includes (e.g. FBSD) */
434 #ifdef WORDS_BIGENDIAN
435   oss->format = AFMT_S16_BE;
436 #else
437   oss->format = AFMT_S16_LE;
438 #endif /* WORDS_BIGENDIAN */  
439 }
440
441 static gboolean 
442 gst_ossformat_get (gint law, gint endianness, gboolean sign, gint width, gint depth,
443                    gint *format, gint *bps) 
444 {
445   if (width != depth) 
446     return FALSE;
447
448   *bps = 1;
449
450   if (law == 0) {
451     if (width == 16) {
452       if (sign == TRUE) {
453         if (endianness == G_LITTLE_ENDIAN) {
454           *format = AFMT_S16_LE;
455           GST_DEBUG (
456                      "16 bit signed LE, no law (%d)", *format);
457         }
458         else if (endianness == G_BIG_ENDIAN) {
459           *format = AFMT_S16_BE;
460           GST_DEBUG (
461                      "16 bit signed BE, no law (%d)", *format);
462         }
463       }
464       else {
465         if (endianness == G_LITTLE_ENDIAN) {
466           *format = AFMT_U16_LE;
467           GST_DEBUG (
468                      "16 bit unsigned LE, no law (%d)", *format);
469         }
470         else if (endianness == G_BIG_ENDIAN) {
471           *format = AFMT_U16_BE;
472           GST_DEBUG (
473                      "16 bit unsigned BE, no law (%d)", *format);
474         }
475       }
476       *bps = 2;
477     }
478     else if (width == 8) {
479       if (sign == TRUE) {
480         *format = AFMT_S8;
481         GST_DEBUG (
482                    "8 bit signed, no law (%d)", *format);
483       }
484       else {
485         *format = AFMT_U8;
486         GST_DEBUG (
487                    "8 bit unsigned, no law (%d)", *format);
488       }
489       *bps = 1;
490     }
491   } else if (law == 1) {
492     *format = AFMT_MU_LAW;
493     GST_DEBUG (
494                "mu law (%d)", *format);
495   } else if (law == 2) {
496     *format = AFMT_A_LAW;
497     GST_DEBUG (
498                "a law (%d)", *format);
499   } else {
500     g_critical ("unknown law");
501     return FALSE;
502   }
503
504   return TRUE;
505 }
506
507 gboolean 
508 gst_osselement_parse_caps (GstOssElement *oss, const GstCaps *caps) 
509 {
510   gint bps, format;
511   GstStructure *structure;
512
513   structure = gst_caps_get_structure (caps, 0);
514
515   gst_structure_get_int  (structure, "width", &oss->width);
516   gst_structure_get_int  (structure, "depth", &oss->depth);
517                 
518   if (oss->width != oss->depth) 
519     return FALSE;
520                   
521   gst_structure_get_int  (structure, "law", &oss->law); 
522   gst_structure_get_int  (structure, "endianness", &oss->endianness);
523   gst_structure_get_boolean  (structure, "signed", &oss->sign);
524                             
525   if (!gst_ossformat_get (oss->law, oss->endianness, oss->sign, 
526                           oss->width, oss->depth, &format, &bps))
527   { 
528      GST_DEBUG ("could not get format");
529      return FALSE;
530   }
531
532   gst_structure_get_int  (structure, "channels", &oss->channels);
533   gst_structure_get_int  (structure, "rate", &oss->rate);
534                               
535   oss->bps = bps * oss->channels * oss->rate;
536   oss->format = format;
537
538   return TRUE;
539 }
540
541 #define GET_FIXED_INT(caps, name, dest)         \
542 G_STMT_START {                                  \
543   if (gst_caps_has_fixed_property (caps, name)) \
544     gst_structure_get_int  (structure, name, dest);        \
545 } G_STMT_END
546 #define GET_FIXED_BOOLEAN(caps, name, dest)     \
547 G_STMT_START {                                  \
548   if (gst_caps_has_fixed_property (caps, name)) \
549     gst_structure_get_boolean  (structure, name, dest);    \
550 } G_STMT_END
551
552 gboolean 
553 gst_osselement_merge_fixed_caps (GstOssElement *oss, GstCaps *caps) 
554 {
555   gint bps, format;
556   GstStructure *structure;
557
558   structure = gst_caps_get_structure (caps, 0);
559   
560   /* peel off fixed stuff from the caps */
561   gst_structure_get_int (structure, "law",        &oss->law);
562   gst_structure_get_int (structure, "endianness", &oss->endianness);
563   gst_structure_get_boolean (structure, "signed",     &oss->sign);
564   gst_structure_get_int (structure, "width",      &oss->width);
565   gst_structure_get_int (structure, "depth",      &oss->depth);
566
567   if (!gst_ossformat_get (oss->law, oss->endianness, oss->sign, 
568                           oss->width, oss->depth, &format, &bps))
569   { 
570      return FALSE;
571   }
572
573   gst_structure_get_int (structure, "rate",       &oss->rate);
574   gst_structure_get_int (structure, "channels",   &oss->channels);
575                               
576   oss->bps = bps * oss->channels * oss->rate;
577   oss->format = format;
578                                           
579   return TRUE;
580 }
581
582 gboolean 
583 gst_osselement_sync_parms (GstOssElement *oss) 
584 {
585   audio_buf_info space;
586   int frag;
587   gint target_format;
588   gint target_channels;
589   gint target_rate;
590   gint fragscale, frag_ln;
591
592   if (oss->fd == -1)
593     return FALSE;
594   
595   if (oss->fragment >> 16)
596     frag = oss->fragment;
597   else
598     frag = 0x7FFF0000 | oss->fragment;
599   
600   GST_INFO ("osselement: setting sound card to %dHz %d format %s (%08x fragment)",
601             oss->rate, oss->format,
602             (oss->channels == 2) ? "stereo" : "mono", frag);
603
604   ioctl (oss->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
605   ioctl (oss->fd, SNDCTL_DSP_RESET, 0);
606
607   target_format   = oss->format;
608   target_channels = oss->channels;
609   target_rate     = oss->rate;
610
611   ioctl (oss->fd, SNDCTL_DSP_SETFMT,   &oss->format);
612   ioctl (oss->fd, SNDCTL_DSP_CHANNELS, &oss->channels);
613   ioctl (oss->fd, SNDCTL_DSP_SPEED,    &oss->rate);
614
615   ioctl (oss->fd, SNDCTL_DSP_GETBLKSIZE, &oss->fragment_size);
616
617   if (oss->mode == GST_OSSELEMENT_WRITE) {
618     ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &space);
619   }
620   else {
621     ioctl (oss->fd, SNDCTL_DSP_GETISPACE, &space);
622   }
623
624   /* calculate new fragment using a poor man's logarithm function */
625   fragscale = 1;
626   frag_ln = 0;
627   while (fragscale < space.fragsize) {
628     fragscale <<= 1;
629     frag_ln++;
630   }
631   oss->fragment = space.fragstotal << 16 | frag_ln;
632           
633   GST_INFO ("osselement: set sound card to %dHz, %d format, %s "
634             "(%d bytes buffer, %08x fragment)",
635             oss->rate, oss->format,
636             (oss->channels == 2) ? "stereo" : "mono", 
637             space.bytes, oss->fragment);
638
639   oss->fragment_time = (GST_SECOND * oss->fragment_size) / oss->bps;
640   GST_INFO ("fragment time %u %" G_GUINT64_FORMAT "\n", 
641             oss->bps, oss->fragment_time);
642
643   if (target_format   != oss->format   ||
644       target_channels != oss->channels ||
645       target_rate     != oss->rate) 
646   {
647     if (target_channels != oss->channels)
648       g_warning ("couldn't set the right number of channels, enjoy the tone difference");
649     if (target_rate != oss->rate)
650       g_warning ("couldn't set the right number of channels, enjoy the speed difference");
651     if (target_format != oss->format)
652       g_warning ("couldn't set requested OSS parameters, enjoy the noise :)");
653     /* we could eventually return FALSE here, or just do some additional tests
654      * to see that the frequencies don't differ too much etc.. */
655   }
656   return TRUE;
657 }
658
659 static gboolean
660 gst_osselement_open_audio (GstOssElement *oss)
661 {
662   gint caps;
663   GstOssOpenMode mode = GST_OSSELEMENT_READ;
664   const GList *padlist;
665
666   g_return_val_if_fail (oss->fd == -1, FALSE);
667   GST_INFO ("osselement: attempting to open sound device");
668
669   /* Ok, so how do we open the device? We assume that we have (max.) one
670    * pad, and if this is a sinkpad, we're osssink (w). else, we're osssrc (r) */
671   padlist = gst_element_get_pad_list (GST_ELEMENT (oss));
672   if (padlist != NULL) {
673     GstPad *firstpad = padlist->data;
674     if (GST_PAD_IS_SINK (firstpad)) {
675       mode = GST_OSSELEMENT_WRITE;
676     }
677   } else {
678     goto do_mixer;
679   }
680
681   /* first try to open the sound card */
682   if (mode == GST_OSSELEMENT_WRITE) {
683     /* open non blocking first so that it returns immediatly with an error
684      * when we cannot get to the device */
685     oss->fd = open (oss->device, O_WRONLY | O_NONBLOCK);
686
687     if (oss->fd >= 0) {
688       close (oss->fd);
689                           
690       /* re-open the sound device in blocking mode */
691       oss->fd = open (oss->device, O_WRONLY);
692     }
693   }
694   else {
695     oss->fd = open (oss->device, O_RDONLY);
696   }
697
698   if (oss->fd < 0) {
699     switch (errno) {
700       case EBUSY:
701         GST_ELEMENT_ERROR (oss, RESOURCE, BUSY,
702                            (_("OSS device \"%s\" is already in use by another program."), oss->device),
703                            (NULL));
704         break;
705       case EACCES:
706       case ETXTBSY:
707         if (mode == GST_OSSELEMENT_WRITE)
708           GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
709                              (_("Could not access device \"%s\", check its permissions."), oss->device),
710                              GST_ERROR_SYSTEM);
711         else
712           GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
713                              (_("Could not access device \"%s\", check its permissions."), oss->device),
714                              GST_ERROR_SYSTEM);
715         break;
716       case ENXIO:
717       case ENODEV:
718       case ENOENT:
719         GST_ELEMENT_ERROR (oss, RESOURCE, NOT_FOUND,
720                            (_("Device \"%s\" does not exist."), oss->device),
721                            GST_ERROR_SYSTEM);
722         break;
723       default:
724         /* FIXME: strerror is not threadsafe */
725         if (mode == GST_OSSELEMENT_WRITE)
726           GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
727                              (_("Could not open device \"%s\" for writing."), oss->device),
728                              GST_ERROR_SYSTEM);
729         else
730           GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
731                              (_("Could not open device \"%s\" for reading."), oss->device),
732                              GST_ERROR_SYSTEM);
733         break;
734     }
735     return FALSE;
736   }
737
738   oss->mode = mode;
739
740   /* we have it, set the default parameters and go have fun */
741   /* set card state */
742   ioctl (oss->fd, SNDCTL_DSP_GETCAPS, &caps);
743
744   GST_INFO ("osselement: Capabilities %08x", caps);
745
746   if (caps & DSP_CAP_DUPLEX)    GST_INFO ( "osselement:   Full duplex");
747   if (caps & DSP_CAP_REALTIME)  GST_INFO ( "osselement:   Realtime");
748   if (caps & DSP_CAP_BATCH)     GST_INFO ( "osselement:   Batch");
749   if (caps & DSP_CAP_COPROC)    GST_INFO ( "osselement:   Has coprocessor");
750   if (caps & DSP_CAP_TRIGGER)   GST_INFO ( "osselement:   Trigger");
751   if (caps & DSP_CAP_MMAP)      GST_INFO ( "osselement:   Direct access");
752
753 #ifdef DSP_CAP_MULTI
754   if (caps & DSP_CAP_MULTI)     GST_INFO ( "osselement:   Multiple open");
755 #endif /* DSP_CAP_MULTI */
756
757 #ifdef DSP_CAP_BIND
758   if (caps & DSP_CAP_BIND)      GST_INFO ( "osselement:   Channel binding");
759 #endif /* DSP_CAP_BIND */
760
761   ioctl(oss->fd, SNDCTL_DSP_GETFMTS, &caps);
762
763   GST_INFO ( "osselement: Formats %08x", caps);
764   if (caps & AFMT_MU_LAW)       GST_INFO ( "osselement:   MU_LAW");
765   if (caps & AFMT_A_LAW)        GST_INFO ( "osselement:   A_LAW");
766   if (caps & AFMT_IMA_ADPCM)    GST_INFO ( "osselement:   IMA_ADPCM");
767   if (caps & AFMT_U8)           GST_INFO ( "osselement:   U8");
768   if (caps & AFMT_S16_LE)       GST_INFO ( "osselement:   S16_LE");
769   if (caps & AFMT_S16_BE)       GST_INFO ( "osselement:   S16_BE");
770   if (caps & AFMT_S8)           GST_INFO ( "osselement:   S8");
771   if (caps & AFMT_U16_LE)       GST_INFO ( "osselement:   U16_LE");
772   if (caps & AFMT_U16_BE)       GST_INFO ( "osselement:   U16_BE");
773   if (caps & AFMT_MPEG)         GST_INFO ( "osselement:   MPEG");
774 #ifdef AFMT_AC3
775   if (caps & AFMT_AC3)          GST_INFO ( "osselement:   AC3");
776 #endif
777
778   GST_INFO ("osselement: opened audio (%s) with fd=%d",
779             oss->device, oss->fd);
780
781   oss->caps = caps;
782
783 do_mixer:
784   gst_ossmixer_build_list (oss);
785
786   return TRUE;
787 }
788
789 static void
790 gst_osselement_close_audio (GstOssElement *oss)
791 {
792   gst_ossmixer_free_list (oss);
793
794   if (oss->fd < 0) 
795     return;
796
797   close(oss->fd);
798   oss->fd = -1;
799 }
800
801 gboolean
802 gst_osselement_convert (GstOssElement *oss,
803                         GstFormat      src_format,
804                         gint64         src_value,
805                         GstFormat     *dest_format,
806                         gint64        *dest_value)
807 {
808   gboolean res = TRUE;
809
810   if (src_format == *dest_format) {
811     *dest_value = src_value;
812     return TRUE;
813   }
814
815   if (oss->bps == 0 || oss->channels == 0 || oss->width == 0)
816     return FALSE;
817
818   switch (src_format) {
819     case GST_FORMAT_BYTES:
820       switch (*dest_format) {
821         case GST_FORMAT_TIME:
822           *dest_value = src_value * GST_SECOND / oss->bps;
823           break;
824         case GST_FORMAT_DEFAULT:
825           *dest_value = src_value / (oss->width * oss->channels / 8);
826           break;
827         default:
828           res = FALSE;
829       }
830       break;
831     case GST_FORMAT_TIME:
832       switch (*dest_format) {
833         case GST_FORMAT_BYTES:
834           *dest_value = src_value * oss->bps / GST_SECOND;
835           break;
836         case GST_FORMAT_DEFAULT:
837           *dest_value = src_value * oss->rate / GST_SECOND;
838           break;
839         default:
840           res = FALSE;
841       }
842       break;
843     case GST_FORMAT_DEFAULT:
844       switch (*dest_format) {
845         case GST_FORMAT_TIME:
846           *dest_value = src_value * GST_SECOND / oss->rate;
847           break;
848         case GST_FORMAT_BYTES:
849           *dest_value = src_value * oss->width * oss->channels / 8;
850           break;
851         default:
852           res = FALSE;
853       }
854       break;
855     default:
856       res = FALSE;
857   }
858
859   return res;
860 }
861
862 static void 
863 gst_osselement_set_property (GObject *object,
864                              guint prop_id,
865                              const GValue *value,
866                              GParamSpec *pspec) 
867 {
868   GstOssElement *oss = GST_OSSELEMENT (object);
869
870   switch (prop_id) {
871     case ARG_DEVICE:
872       /* disallow changing the device while it is opened
873          get_property("device") should return the right one */
874       if (gst_element_get_state (GST_ELEMENT (oss)) == GST_STATE_NULL) {
875         g_free (oss->device);
876         oss->device = g_strdup (g_value_get_string (value));
877
878         /* let's assume that if we have a device map for the mixer,
879          * we're allowed to do all that automagically here */
880         if (GST_OSSELEMENT_GET_CLASS (oss)->device_combinations != NULL) {
881           GList *list = GST_OSSELEMENT_GET_CLASS (oss)->device_combinations;
882
883           while (list) {
884             GstOssDeviceCombination *combi = list->data;
885
886             if (!strcmp (combi->dsp, oss->device)) {
887               g_free (oss->mixer_dev);
888               oss->mixer_dev = g_strdup (combi->mixer);
889               break;
890             }
891
892             list = list->next;
893           }
894         }
895       }
896       break;
897     case ARG_MIXERDEV:
898       /* disallow changing the device while it is opened
899          get_property("mixerdev") should return the right one */
900       if (gst_element_get_state (GST_ELEMENT (oss)) == GST_STATE_NULL) {
901         g_free (oss->mixer_dev);
902         oss->mixer_dev = g_strdup (g_value_get_string (value));
903       }
904       break;
905     default:
906       break;
907   }
908 }
909
910 static void 
911 gst_osselement_get_property (GObject *object,
912                              guint prop_id,
913                              GValue *value,
914                              GParamSpec *pspec) 
915 {
916   GstOssElement *oss = GST_OSSELEMENT (object);
917
918   switch (prop_id) {
919     case ARG_DEVICE:
920       g_value_set_string (value, oss->device);
921       break;
922     case ARG_MIXERDEV:
923       g_value_set_string (value, oss->mixer_dev);
924       break;
925     case ARG_DEVICE_NAME:
926       g_value_set_string (value, oss->device_name);
927       break;
928     default:
929       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
930       break;
931   }
932 }
933
934 static GstElementStateReturn 
935 gst_osselement_change_state (GstElement *element) 
936 {
937   GstOssElement *oss = GST_OSSELEMENT (element);
938
939   switch (GST_STATE_TRANSITION (element)) {
940     case GST_STATE_NULL_TO_READY:
941       if (!gst_osselement_open_audio (oss)) {
942         return GST_STATE_FAILURE;
943       }
944       GST_INFO ("osselement: opened sound device");
945       break;
946     case GST_STATE_READY_TO_NULL:
947       gst_osselement_close_audio (oss);
948       gst_osselement_reset (oss);
949       GST_INFO ("osselement: closed sound device");
950       break;
951     default:
952       break;
953   }
954       
955   if (GST_ELEMENT_CLASS (parent_class)->change_state)
956     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
957
958   return GST_STATE_SUCCESS;
959 }