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