Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / sys / oss4 / oss4-audio.c
1 /* GStreamer OSS4 audio plugin
2  * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30
31 #include "gst/gst-i18n-plugin.h"
32 #include <gst/audio/multichannel.h>
33
34 #include "oss4-audio.h"
35 #include "oss4-mixer.h"
36 #include "oss4-property-probe.h"
37 #include "oss4-sink.h"
38 #include "oss4-source.h"
39 #include "oss4-soundcard.h"
40
41 GST_DEBUG_CATEGORY (oss4mixer_debug);
42 GST_DEBUG_CATEGORY (oss4sink_debug);
43 GST_DEBUG_CATEGORY (oss4src_debug);
44 GST_DEBUG_CATEGORY (oss4_debug);
45
46 #define GST_CAT_DEFAULT oss4_debug
47
48 typedef struct
49 {
50   const GstBufferFormat gst_fmt;
51   const gint oss_fmt;
52   const gchar name[16];
53   const gint depth;
54   const gint width;
55   const gint endianness;
56   const gboolean signedness;
57 } GstOss4AudioFormat;
58
59 /* *INDENT-OFF* */
60 static const GstOss4AudioFormat fmt_map[] = {
61   /* note: keep sorted by preference, prefered formats first */
62   {
63   GST_MU_LAW, AFMT_MU_LAW, "audio/x-mulaw", 0, 0, 0, FALSE}, {
64   GST_A_LAW, AFMT_A_LAW, "audio/x-alaw", 0, 0, 0, FALSE}, {
65   GST_S32_LE, AFMT_S32_LE, "audio/x-raw-int", 32, 32, G_LITTLE_ENDIAN, TRUE}, {
66   GST_S32_BE, AFMT_S32_BE, "audio/x-raw-int", 32, 32, G_BIG_ENDIAN, TRUE}, {
67   GST_S24_LE, AFMT_S24_LE, "audio/x-raw-int", 24, 32, G_LITTLE_ENDIAN, TRUE}, {
68   GST_S24_BE, AFMT_S24_BE, "audio/x-raw-int", 24, 32, G_BIG_ENDIAN, TRUE}, {
69   GST_S24_3LE, AFMT_S24_PACKED, "audio/x-raw-int", 24, 24, G_LITTLE_ENDIAN,
70         TRUE}, {
71   GST_S16_LE, AFMT_S16_LE, "audio/x-raw-int", 16, 16, G_LITTLE_ENDIAN, TRUE}, {
72   GST_S16_BE, AFMT_S16_BE, "audio/x-raw-int", 16, 16, G_BIG_ENDIAN, TRUE}, {
73   GST_U16_LE, AFMT_U16_LE, "audio/x-raw-int", 16, 16, G_LITTLE_ENDIAN, FALSE}, {
74   GST_U16_BE, AFMT_U16_BE, "audio/x-raw-int", 16, 16, G_BIG_ENDIAN, FALSE}, {
75   GST_S8, AFMT_S8, "audio/x-raw-int", 8, 8, 0, TRUE}, {
76   GST_U8, AFMT_U8, "audio/x-raw-int", 8, 8, 0, FALSE}
77 };
78 /* *INDENT-ON* */
79
80 /* formats we assume the OSS4 layer can always handle and convert internally */
81 #define CONVERTIBLE_FORMATS (   \
82     AFMT_MU_LAW | AFMT_A_LAW |  \
83     AFMT_S32_LE | AFMT_S32_BE | \
84     AFMT_S24_LE | AFMT_S24_BE | \
85     AFMT_S24_PACKED |           \
86     AFMT_S16_LE | AFMT_S16_BE | \
87     AFMT_U16_LE | AFMT_U16_BE | \
88     AFMT_S8 | AFMT_U8 )
89
90 static void
91 gst_oss4_append_format_to_caps (const GstOss4AudioFormat * fmt, GstCaps * caps)
92 {
93   GstStructure *s;
94
95   s = gst_structure_empty_new (fmt->name);
96   if (fmt->width != 0 && fmt->depth != 0) {
97     gst_structure_set (s, "width", G_TYPE_INT, fmt->width, "depth", G_TYPE_INT,
98         fmt->depth, "signed", G_TYPE_BOOLEAN, fmt->signedness, NULL);
99   }
100   if (fmt->endianness != 0) {
101     gst_structure_set (s, "endianness", G_TYPE_INT, fmt->endianness, NULL);
102   }
103   gst_caps_append_structure (caps, s);
104 }
105
106 static gint
107 gst_oss4_audio_get_oss_format (GstBufferFormat fmt)
108 {
109   guint i;
110
111   for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
112     if (fmt_map[i].gst_fmt == fmt)
113       return fmt_map[i].oss_fmt;
114   }
115   return 0;
116 }
117
118 /* These are pretty random */
119 #define GST_OSS4_MIN_SAMPLE_RATE 1
120 #define GST_OSS4_MAX_SAMPLE_RATE 192000
121
122 static gboolean
123 gst_oss4_audio_detect_rates (GstObject * obj, oss_audioinfo * ai,
124     GstCaps * caps)
125 {
126   GValue val = { 0, };
127   int minrate, maxrate, i;
128
129   minrate = ai->min_rate;
130   maxrate = ai->max_rate;
131
132   /* sanity check */
133   if (minrate > maxrate) {
134     GST_WARNING_OBJECT (obj, "min_rate %d > max_rate %d (buggy driver?)",
135         minrate, maxrate);
136     maxrate = ai->min_rate;     /* swap */
137     minrate = ai->max_rate;
138   }
139
140   /* limit to something sensible */
141   if (minrate < GST_OSS4_MIN_SAMPLE_RATE)
142     minrate = GST_OSS4_MIN_SAMPLE_RATE;
143   if (maxrate > GST_OSS4_MAX_SAMPLE_RATE)
144     maxrate = GST_OSS4_MAX_SAMPLE_RATE;
145
146   if (maxrate < GST_OSS4_MIN_SAMPLE_RATE) {
147     GST_WARNING_OBJECT (obj, "max_rate < %d, which makes no sense",
148         GST_OSS4_MIN_SAMPLE_RATE);
149     return FALSE;
150   }
151
152   GST_LOG_OBJECT (obj, "min_rate %d, max_rate %d (originally: %d, %d)",
153       minrate, maxrate, ai->min_rate, ai->max_rate);
154
155   if ((ai->caps & PCM_CAP_FREERATE)) {
156     GST_LOG_OBJECT (obj, "device supports any sample rate between min and max");
157     if (minrate == maxrate) {
158       g_value_init (&val, G_TYPE_INT);
159       g_value_set_int (&val, maxrate);
160     } else {
161       g_value_init (&val, GST_TYPE_INT_RANGE);
162       gst_value_set_int_range (&val, minrate, maxrate);
163     }
164   } else {
165     GST_LOG_OBJECT (obj, "%d sample rates:", ai->nrates);
166     g_value_init (&val, GST_TYPE_LIST);
167     for (i = 0; i < ai->nrates; ++i) {
168       GST_LOG_OBJECT (obj, " rate: %d", ai->rates[i]);
169
170       if (ai->rates[i] >= minrate && ai->rates[i] <= maxrate) {
171         GValue rate_val = { 0, };
172
173         g_value_init (&rate_val, G_TYPE_INT);
174         g_value_set_int (&rate_val, ai->rates[i]);
175         gst_value_list_append_value (&val, &rate_val);
176         g_value_unset (&rate_val);
177       }
178     }
179
180     if (gst_value_list_get_size (&val) == 0) {
181       g_value_unset (&val);
182       return FALSE;
183     }
184   }
185
186   for (i = 0; i < gst_caps_get_size (caps); ++i) {
187     GstStructure *s;
188
189     s = gst_caps_get_structure (caps, i);
190     gst_structure_set_value (s, "rate", &val);
191   }
192
193   g_value_unset (&val);
194
195   return TRUE;
196 }
197
198 static void
199 gst_oss4_audio_add_channel_layout (GstObject * obj, guint64 layout,
200     guint num_channels, GstStructure * s)
201 {
202   const GstAudioChannelPosition pos_map[16] = {
203     GST_AUDIO_CHANNEL_POSITION_NONE,    /* 0 = dunno          */
204     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,      /* 1 = left           */
205     GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,     /* 2 = right          */
206     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,    /* 3 = center         */
207     GST_AUDIO_CHANNEL_POSITION_LFE,     /* 4 = lfe            */
208     GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,       /* 5 = left surround  */
209     GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,      /* 6 = right surround */
210     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,       /* 7 = left rear      */
211     GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,      /* 8 = right rear     */
212     GST_AUDIO_CHANNEL_POSITION_NONE,
213     GST_AUDIO_CHANNEL_POSITION_NONE,
214     GST_AUDIO_CHANNEL_POSITION_NONE,
215     GST_AUDIO_CHANNEL_POSITION_NONE,
216     GST_AUDIO_CHANNEL_POSITION_NONE,
217     GST_AUDIO_CHANNEL_POSITION_NONE,
218     GST_AUDIO_CHANNEL_POSITION_NONE
219   };
220   GstAudioChannelPosition ch_layout[8] = { 0, };
221   guint speaker_pos;            /* speaker position as defined by OSS */
222   guint i;
223
224   g_return_if_fail (num_channels <= G_N_ELEMENTS (ch_layout));
225
226   for (i = 0; i < num_channels; ++i) {
227     /* layout contains up to 16 speaker positions, with each taking up 4 bits */
228     speaker_pos = (guint) ((layout >> (i * 4)) & 0x0f);
229
230     /* if it's a channel position that's unknown to us, set all to NONE and
231      * bail out */
232     if (G_UNLIKELY (pos_map[speaker_pos] == GST_AUDIO_CHANNEL_POSITION_NONE))
233       goto no_layout;
234
235     ch_layout[i] = pos_map[speaker_pos];
236   }
237   gst_audio_set_channel_positions (s, ch_layout);
238   return;
239
240 no_layout:
241   {
242     /* only warn if it's really unknown, position 0 is ok and represents NONE
243      * (in which case we also just set all others to NONE ignoring the other
244      * positions in the OSS-given layout, because that's what we currently
245      * require in GStreamer) */
246     if (speaker_pos != 0) {
247       GST_WARNING_OBJECT (obj, "unknown OSS channel position %x", ch_layout[i]);
248     }
249     for (i = 0; i < num_channels; ++i) {
250       ch_layout[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
251     }
252     gst_audio_set_channel_positions (s, ch_layout);
253     return;
254   }
255 }
256
257 /* arbitrary max. limit */
258 #define GST_OSS4_MIN_CHANNELS 1
259 #define GST_OSS4_MAX_CHANNELS 4096
260
261 /* takes ownership of the input caps */
262 static GstCaps *
263 gst_oss4_audio_detect_channels (GstObject * obj, int fd, oss_audioinfo * ai,
264     GstCaps * in_caps)
265 {
266   const gchar *forced_layout;
267   GstStructure *s = NULL;
268   guint64 layout = 0;
269   GstCaps *chan_caps = NULL;
270   GstCaps *out_caps = NULL;
271   int minchans, maxchans;
272   int c, i, j;
273
274   /* GST_OSS4_CHANNEL_LAYOUT environment variable: may be used to force a
275    * particular channel layout (if it contains an odd number of channel
276    * positions it will also make us advertise a channel layout for that
277    * channel count, even if we'd usually skip it; this is especially useful
278    * for folks with 2.1 speakers, I guess) */
279   forced_layout = g_getenv ("GST_OSS4_CHANNEL_LAYOUT");
280
281   minchans = ai->min_channels;
282   maxchans = ai->max_channels;
283
284   /* sanity check */
285   if (minchans > maxchans) {
286     GST_WARNING_OBJECT (obj, "min_chans %d > max_chans %d (buggy driver?)",
287         minchans, maxchans);
288     maxchans = ai->min_channels;        /* swap */
289     minchans = ai->max_channels;
290   }
291
292   /* limit to something sensible */
293   if (minchans < GST_OSS4_MIN_CHANNELS)
294     minchans = GST_OSS4_MIN_CHANNELS;
295   if (maxchans > GST_OSS4_MAX_CHANNELS)
296     maxchans = GST_OSS4_MAX_CHANNELS;
297
298   if (maxchans < GST_OSS4_MIN_CHANNELS) {
299     GST_WARNING_OBJECT (obj, "max_chans < %d, which makes no sense",
300         GST_OSS4_MIN_CHANNELS);
301     gst_caps_unref (in_caps);
302     return NULL;
303   }
304
305   GST_LOG_OBJECT (obj, "min_channels %d, max_channels %d (originally: %d, %d)",
306       minchans, maxchans, ai->min_channels, ai->max_channels);
307
308   chan_caps = gst_caps_new_empty ();
309
310   /* first do the simple cases: mono + stereo (channel layout implied) */
311   if (minchans == 1 && maxchans == 1)
312     s = gst_structure_new ("x", "channels", G_TYPE_INT, 1, NULL);
313   else if (minchans == 2 && maxchans >= 2)
314     s = gst_structure_new ("x", "channels", G_TYPE_INT, 2, NULL);
315   else if (minchans == 1 && maxchans >= 2)
316     s = gst_structure_new ("x", "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
317   gst_caps_append_structure (chan_caps, s);
318   s = NULL;
319
320   /* TODO: we assume all drivers use a left/right layout for stereo here */
321   if (maxchans <= 2)
322     goto done;
323
324   if (ioctl (fd, SNDCTL_DSP_GET_CHNORDER, &layout) == -1) {
325     GST_WARNING_OBJECT (obj, "couldn't query channel layout, assuming default");
326     layout = CHNORDER_NORMAL;
327   }
328   GST_DEBUG_OBJECT (obj, "channel layout: %08" G_GINT64_MODIFIER "x", layout);
329
330   /* e.g. forced 2.1 layout would be GST_OSS4_CHANNEL_LAYOUT=421 */
331   if (forced_layout != NULL && *forced_layout != '\0') {
332     guint layout_len;
333
334     layout_len = strlen (forced_layout);
335     if (layout_len >= minchans && layout_len <= maxchans) {
336       layout = g_ascii_strtoull (forced_layout, NULL, 16);
337       maxchans = layout_len;
338       GST_DEBUG_OBJECT (obj, "forced channel layout: %08" G_GINT64_MODIFIER "x"
339           " ('%s'), maxchans now %d", layout, forced_layout, maxchans);
340     } else {
341       GST_WARNING_OBJECT (obj, "ignoring forced channel layout: layout has %d "
342           "channel positions but maxchans is %d", layout_len, maxchans);
343     }
344   }
345
346   /* need to advertise channel layouts for anything >2 and <=8 channels */
347   for (c = MAX (3, minchans); c <= MIN (maxchans, 8); c++) {
348     /* "The min_channels and max_channels fields define the limits for the
349      * number of channels. However some devices don't support all channels
350      * within this range. It's possible that the odd values (3, 5, 7, 9, etc).
351      * are not supported. There is currently no way to check for this other
352      * than checking if SNDCTL_DSP_CHANNELS accepts the requested value.
353      * Another approach is trying to avoid using odd number of channels."
354      *
355      * So, we don't know for sure if these odd values are supported:
356      */
357     if ((c == 3 || c == 5 || c == 7) && (c != maxchans)) {
358       GST_LOG_OBJECT (obj, "not adding layout with %d channels", c);
359       continue;
360     }
361
362     s = gst_structure_new ("x", "channels", G_TYPE_INT, c, NULL);
363     gst_oss4_audio_add_channel_layout (obj, layout, c, s);
364     GST_LOG_OBJECT (obj, "c=%u, appending struct %" GST_PTR_FORMAT, c, s);
365     gst_caps_append_structure (chan_caps, s);
366     s = NULL;
367   }
368
369   if (maxchans <= 8)
370     goto done;
371
372   /* for everything >8 channels, CHANNEL_POSITION_NONE is implied. */
373   if (minchans == maxchans || maxchans == 9) {
374     s = gst_structure_new ("x", "channels", G_TYPE_INT, maxchans, NULL);
375   } else {
376     s = gst_structure_new ("x", "channels", GST_TYPE_INT_RANGE,
377         MAX (9, minchans), maxchans, NULL);
378   }
379   gst_caps_append_structure (chan_caps, s);
380   s = NULL;
381
382 done:
383
384   GST_LOG_OBJECT (obj, "channel structures: %" GST_PTR_FORMAT, chan_caps);
385
386   out_caps = gst_caps_new_empty ();
387
388   /* combine each structure in the input caps with each channel caps struct */
389   for (i = 0; i < gst_caps_get_size (in_caps); ++i) {
390     const GstStructure *in_s;
391
392     in_s = gst_caps_get_structure (in_caps, i);
393
394     for (j = 0; j < gst_caps_get_size (chan_caps); ++j) {
395       const GstStructure *chan_s;
396       const GValue *val;
397
398       s = gst_structure_copy (in_s);
399       chan_s = gst_caps_get_structure (chan_caps, j);
400       if ((val = gst_structure_get_value (chan_s, "channels")))
401         gst_structure_set_value (s, "channels", val);
402       if ((val = gst_structure_get_value (chan_s, "channel-positions")))
403         gst_structure_set_value (s, "channel-positions", val);
404
405       gst_caps_append_structure (out_caps, s);
406       s = NULL;
407     }
408   }
409
410   gst_caps_unref (in_caps);
411   gst_caps_unref (chan_caps);
412   return out_caps;
413 }
414
415 GstCaps *
416 gst_oss4_audio_probe_caps (GstObject * obj, int fd)
417 {
418   oss_audioinfo ai = { 0, };
419   gboolean output;
420   GstCaps *caps;
421   int nonnative_formats = 0;
422   int formats, i;
423
424   output = GST_IS_OSS4_SINK (obj);
425
426   /* -1 = get info for currently open device (fd). This will fail with
427    * OSS build <= 1013 because of a bug in OSS */
428   ai.dev = -1;
429   if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == -1)
430     goto engineinfo_failed;
431
432   formats = (output) ? ai.oformats : ai.iformats;
433
434   GST_LOG_OBJECT (obj, "%s formats : 0x%08x", (output) ? "out" : "in", formats);
435
436   caps = gst_caps_new_empty ();
437
438   /* first list all the formats natively supported */
439   for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
440     if ((formats & fmt_map[i].oss_fmt)) {
441       gst_oss4_append_format_to_caps (&fmt_map[i], caps);
442     } else if ((fmt_map[i].oss_fmt & CONVERTIBLE_FORMATS)) {
443       nonnative_formats |= fmt_map[i].oss_fmt;
444     }
445   }
446
447   GST_LOG_OBJECT (obj, "adding non-native %s formats : 0x%08x",
448       (output) ? "out" : "in", nonnative_formats);
449
450   /* now append non-native formats for which conversion would be needed */
451   for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
452     if ((nonnative_formats & fmt_map[i].oss_fmt)) {
453       gst_oss4_append_format_to_caps (&fmt_map[i], caps);
454     }
455   }
456
457   gst_caps_do_simplify (caps);
458   GST_LOG_OBJECT (obj, "formats: %" GST_PTR_FORMAT, caps);
459
460   if (!gst_oss4_audio_detect_rates (obj, &ai, caps))
461     goto detect_rates_failed;
462
463   caps = gst_oss4_audio_detect_channels (obj, fd, &ai, caps);
464   if (caps == NULL)
465     goto detect_channels_failed;
466
467   GST_LOG_OBJECT (obj, "probed caps: %" GST_PTR_FORMAT, caps);
468
469   return caps;
470
471 /* ERRORS */
472 engineinfo_failed:
473   {
474     GST_WARNING ("ENGINEINFO supported formats probe failed: %s",
475         g_strerror (errno));
476     return NULL;
477   }
478 detect_rates_failed:
479   {
480     GST_WARNING_OBJECT (obj, "failed to detect supported sample rates");
481     gst_caps_unref (caps);
482     return NULL;
483   }
484 detect_channels_failed:
485   {
486     GST_WARNING_OBJECT (obj, "failed to detect supported channels");
487     gst_caps_unref (caps);
488     return NULL;
489   }
490 }
491
492 GstCaps *
493 gst_oss4_audio_get_template_caps (void)
494 {
495   GstCaps *caps;
496   gint i;
497
498   caps = gst_caps_new_empty ();
499
500   for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
501     gst_oss4_append_format_to_caps (&fmt_map[i], caps);
502   }
503
504   gst_caps_do_simplify (caps);
505
506   for (i = 0; i < gst_caps_get_size (caps); ++i) {
507     GstStructure *s;
508
509     s = gst_caps_get_structure (caps, i);
510     gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, GST_OSS4_MIN_SAMPLE_RATE,
511         GST_OSS4_MAX_SAMPLE_RATE, "channels", GST_TYPE_INT_RANGE,
512         GST_OSS4_MIN_CHANNELS, GST_OSS4_MAX_CHANNELS, NULL);
513   }
514
515   return caps;
516 }
517
518 /* called by gst_oss4_sink_prepare() and gst_oss4_source_prepare() */
519 gboolean
520 gst_oss4_audio_set_format (GstObject * obj, int fd, GstRingBufferSpec * spec)
521 {
522   struct audio_buf_info info = { 0, };
523   int fmt, chans, rate;
524
525   fmt = gst_oss4_audio_get_oss_format (spec->format);
526   if (fmt == 0)
527     goto wrong_format;
528
529   if (spec->type == GST_BUFTYPE_LINEAR && spec->width != 32 &&
530       spec->width != 24 && spec->width != 16 && spec->width != 8) {
531     goto dodgy_width;
532   }
533
534   /* format */
535   GST_LOG_OBJECT (obj, "setting format: %d", fmt);
536   if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) == -1)
537     goto set_format_failed;
538
539   /* channels */
540   GST_LOG_OBJECT (obj, "setting channels: %d", spec->channels);
541   chans = spec->channels;
542   if (ioctl (fd, SNDCTL_DSP_CHANNELS, &chans) == -1)
543     goto set_channels_failed;
544
545   /* rate */
546   GST_LOG_OBJECT (obj, "setting rate: %d", spec->rate);
547   rate = spec->rate;
548   if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) == -1)
549     goto set_rate_failed;
550
551   GST_DEBUG_OBJECT (obj, "effective format   : %d", fmt);
552   GST_DEBUG_OBJECT (obj, "effective channels : %d", chans);
553   GST_DEBUG_OBJECT (obj, "effective rate     : %d", rate);
554
555   /* make sure format, channels, and rate are the ones we requested */
556   if (fmt != gst_oss4_audio_get_oss_format (spec->format) ||
557       chans != spec->channels || rate != spec->rate) {
558     /* This shouldn't happen, but hey */
559     goto format_not_what_was_requested;
560   }
561
562   if (GST_IS_OSS4_SOURCE (obj)) {
563     if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) == -1)
564       goto get_ispace_failed;
565   } else {
566     if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
567       goto get_ospace_failed;
568   }
569
570   spec->segsize = info.fragsize;
571
572   /* we add some extra fragments -- this helps us account for delays due to
573    * conversion buffer, streams queueing, etc.  It is important that these
574    * be taken into account because otherwise the delay counter can wind up
575    * being too large, and the buffer will wrap.  */
576   spec->segtotal = info.fragstotal + 4;
577
578   spec->bytes_per_sample = (spec->width / 8) * spec->channels;
579
580   GST_DEBUG_OBJECT (obj, "got segsize: %d, segtotal: %d, value: %08x",
581       spec->segsize, spec->segtotal, info.fragsize);
582
583   return TRUE;
584
585 /* ERRORS */
586 wrong_format:
587   {
588     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
589         ("Unable to get format %d", spec->format));
590     return FALSE;
591   }
592 dodgy_width:
593   {
594     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
595         ("unexpected width %d", spec->width));
596     return FALSE;
597   }
598 set_format_failed:
599   {
600     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
601         ("DSP_SETFMT(%d) failed: %s", fmt, g_strerror (errno)));
602     return FALSE;
603   }
604 set_channels_failed:
605   {
606     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
607         ("DSP_CHANNELS(%d) failed: %s", chans, g_strerror (errno)));
608     return FALSE;
609   }
610 set_rate_failed:
611   {
612     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
613         ("DSP_SPEED(%d) failed: %s", rate, g_strerror (errno)));
614     return FALSE;
615   }
616 get_ospace_failed:
617   {
618     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
619         ("DSP_GETOSPACE failed: %s", g_strerror (errno)));
620     return FALSE;
621   }
622 get_ispace_failed:
623   {
624     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
625         ("DSP_GETISPACE failed: %s", g_strerror (errno)));
626     return FALSE;
627   }
628 format_not_what_was_requested:
629   {
630     GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
631         ("Format actually configured wasn't the one we requested. This is "
632             "probably either a bug in the driver or in the format probing code."));
633     return FALSE;
634   }
635 }
636
637 int
638 gst_oss4_audio_get_version (GstObject * obj, int fd)
639 {
640   gint ver = 0;
641
642   /* we use the old ioctl here on purpose instead of SNDCTL_SYSINFO */
643   if (ioctl (fd, OSS_GETVERSION, &ver) < 0) {
644     GST_LOG_OBJECT (obj, "OSS_GETVERSION failed: %s", g_strerror (errno));
645     return -1;
646   }
647   GST_LOG_OBJECT (obj, "OSS version: 0x%08x", ver);
648   return ver;
649 }
650
651 gboolean
652 gst_oss4_audio_check_version (GstObject * obj, int fd)
653 {
654   return (gst_oss4_audio_get_version (obj, fd) >= GST_MIN_OSS4_VERSION);
655 }
656
657 gchar *
658 gst_oss4_audio_find_device (GstObject * oss)
659 {
660   GValueArray *arr;
661   gchar *ret = NULL;
662
663   arr = gst_property_probe_probe_and_get_values_name (GST_PROPERTY_PROBE (oss),
664       "device");
665
666   if (arr != NULL) {
667     if (arr->n_values > 0) {
668       const GValue *val;
669
670       val = g_value_array_get_nth (arr, 0);
671       ret = g_value_dup_string (val);
672     }
673     g_value_array_free (arr);
674   }
675
676   GST_LOG_OBJECT (oss, "first device found: %s", GST_STR_NULL (ret));
677
678   return ret;
679 }
680
681 static gboolean
682 plugin_init (GstPlugin * plugin)
683 {
684   gint rank;
685
686   GST_DEBUG_CATEGORY_INIT (oss4sink_debug, "oss4sink", 0, "OSS4 audio sink");
687   GST_DEBUG_CATEGORY_INIT (oss4src_debug, "oss4src", 0, "OSS4 audio src");
688   GST_DEBUG_CATEGORY_INIT (oss4mixer_debug, "oss4mixer", 0, "OSS4 mixer");
689   GST_DEBUG_CATEGORY_INIT (oss4_debug, "oss4", 0, "OSS4 plugin");
690
691 #ifdef ENABLE_NLS
692   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
693       LOCALEDIR);
694   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
695   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
696 #endif
697
698   /* we want a higher rank than the legacy OSS elements have now */
699   rank = GST_RANK_SECONDARY + 1;
700
701   if (!gst_element_register (plugin, "oss4sink", rank, GST_TYPE_OSS4_SINK) ||
702       !gst_element_register (plugin, "oss4src", rank, GST_TYPE_OSS4_SOURCE) ||
703       !gst_element_register (plugin, "oss4mixer", rank, GST_TYPE_OSS4_MIXER)) {
704     return FALSE;
705   }
706
707   return TRUE;
708 }
709
710 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
711     GST_VERSION_MINOR,
712     "oss4",
713     "Open Sound System (OSS) version 4 support for GStreamer",
714     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)