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