Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / sys / oss / gstosshelper.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gstosshelper.c: OSS helper routines
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 <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35
36 #ifdef HAVE_OSS_INCLUDE_IN_SYS
37 # include <sys/soundcard.h>
38 #else
39 # ifdef HAVE_OSS_INCLUDE_IN_ROOT
40 #  include <soundcard.h>
41 # else
42 #  ifdef HAVE_OSS_INCLUDE_IN_MACHINE
43 #   include <machine/soundcard.h>
44 #  else
45 #   error "What to include?"
46 #  endif /* HAVE_OSS_INCLUDE_IN_MACHINE */
47 # endif /* HAVE_OSS_INCLUDE_IN_ROOT */
48 #endif /* HAVE_OSS_INCLUDE_IN_SYS */
49
50 #include <gst/interfaces/propertyprobe.h>
51
52 #include "gstosshelper.h"
53 #include "gstossmixer.h"
54
55 GST_DEBUG_CATEGORY_EXTERN (oss_debug);
56 #define GST_CAT_DEFAULT oss_debug
57
58 typedef struct _GstOssProbe GstOssProbe;
59 struct _GstOssProbe
60 {
61   int fd;
62   int format;
63   int n_channels;
64   GArray *rates;
65   int min;
66   int max;
67 };
68
69 typedef struct _GstOssRange GstOssRange;
70 struct _GstOssRange
71 {
72   int min;
73   int max;
74 };
75
76 static GstStructure *gst_oss_helper_get_format_structure (unsigned int
77     format_bit);
78 static gboolean gst_oss_helper_rate_probe_check (GstOssProbe * probe);
79 static int gst_oss_helper_rate_check_rate (GstOssProbe * probe, int irate);
80 static void gst_oss_helper_rate_add_range (GQueue * queue, int min, int max);
81 static void gst_oss_helper_rate_add_rate (GArray * array, int rate);
82 static int gst_oss_helper_rate_int_compare (gconstpointer a, gconstpointer b);
83
84 GstCaps *
85 gst_oss_helper_probe_caps (gint fd)
86 {
87   GstOssProbe *probe;
88   int i;
89   gboolean ret;
90   GstStructure *structure;
91   unsigned int format_bit;
92   unsigned int format_mask;
93   GstCaps *caps;
94
95   /* FIXME test make sure we're not currently playing */
96   /* FIXME test both mono and stereo */
97
98   format_mask = AFMT_U8 | AFMT_S8;
99
100   if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
101     format_mask |= AFMT_S16_LE | AFMT_U16_LE;
102   else
103     format_mask |= AFMT_S16_BE | AFMT_U16_BE;
104
105   caps = gst_caps_new_empty ();
106
107   /* assume that the most significant bit of format_mask is 0 */
108   for (format_bit = 1 << 31; format_bit > 0; format_bit >>= 1) {
109     if (format_bit & format_mask) {
110       GValue rate_value = { 0 };
111
112       probe = g_new0 (GstOssProbe, 1);
113       probe->fd = fd;
114       probe->format = format_bit;
115       /* FIXME: this is not working for all cards, see bug #518474 */
116       probe->n_channels = 2;
117
118       ret = gst_oss_helper_rate_probe_check (probe);
119       if (probe->min == -1 || probe->max == -1) {
120         g_array_free (probe->rates, TRUE);
121         g_free (probe);
122         continue;
123       }
124
125       if (ret) {
126         GValue value = { 0 };
127
128         g_array_sort (probe->rates, gst_oss_helper_rate_int_compare);
129
130         g_value_init (&rate_value, GST_TYPE_LIST);
131         g_value_init (&value, G_TYPE_INT);
132
133         for (i = 0; i < probe->rates->len; i++) {
134           g_value_set_int (&value, g_array_index (probe->rates, int, i));
135
136           gst_value_list_append_value (&rate_value, &value);
137         }
138
139         g_value_unset (&value);
140       } else {
141         /* one big range */
142         g_value_init (&rate_value, GST_TYPE_INT_RANGE);
143         gst_value_set_int_range (&rate_value, probe->min, probe->max);
144       }
145
146       g_array_free (probe->rates, TRUE);
147       g_free (probe);
148
149       structure = gst_oss_helper_get_format_structure (format_bit);
150       gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
151       gst_structure_set_value (structure, "rate", &rate_value);
152       g_value_unset (&rate_value);
153
154       gst_caps_append_structure (caps, structure);
155     }
156   }
157
158   if (gst_caps_is_empty (caps)) {
159     /* fixme: make user-visible */
160     GST_WARNING ("Your OSS device could not be probed correctly");
161   }
162
163   GST_DEBUG ("probed caps: %" GST_PTR_FORMAT, caps);
164
165   return caps;
166 }
167
168 static GstStructure *
169 gst_oss_helper_get_format_structure (unsigned int format_bit)
170 {
171   GstStructure *structure;
172   const gchar *format;
173
174   switch (format_bit) {
175     case AFMT_U8:
176       format = "U8";
177       break;
178     case AFMT_S16_LE:
179       format = "S16_LE";
180       break;
181     case AFMT_S16_BE:
182       format = "S16_BE";
183       break;
184     case AFMT_S8:
185       format = "S8";
186       break;
187     case AFMT_U16_LE:
188       format = "U16_LE";
189       break;
190     case AFMT_U16_BE:
191       format = "U16_BE";
192       break;
193     default:
194       g_assert_not_reached ();
195       return NULL;
196   }
197
198   structure = gst_structure_new ("audio/x-raw",
199       "format", G_TYPE_STRING, format, NULL);
200
201   return structure;
202 }
203
204 static gboolean
205 gst_oss_helper_rate_probe_check (GstOssProbe * probe)
206 {
207   GstOssRange *range;
208   GQueue *ranges;
209   int exact_rates = 0;
210   gboolean checking_exact_rates = TRUE;
211   int n_checks = 0;
212   gboolean result = TRUE;
213
214   ranges = g_queue_new ();
215
216   probe->rates = g_array_new (FALSE, FALSE, sizeof (int));
217
218   probe->min = gst_oss_helper_rate_check_rate (probe, 1000);
219   n_checks++;
220   probe->max = gst_oss_helper_rate_check_rate (probe, 100000);
221   /* a little bug workaround */
222   {
223     int max;
224
225     max = gst_oss_helper_rate_check_rate (probe, 48000);
226     if (max > probe->max) {
227       GST_ERROR
228           ("Driver bug recognized (driver does not round rates correctly).  Please file a bug report.");
229       probe->max = max;
230     }
231   }
232   n_checks++;
233   if (probe->min == -1 || probe->max == -1) {
234     /* This is a workaround for drivers that return -EINVAL (or another
235      * error) for rates outside of [8000,48000].  If this fails, the
236      * driver is seriously buggy, and probably doesn't work with other
237      * media libraries/apps.  */
238     probe->min = gst_oss_helper_rate_check_rate (probe, 8000);
239     probe->max = gst_oss_helper_rate_check_rate (probe, 48000);
240   }
241   if (probe->min == -1 || probe->max == -1) {
242     GST_DEBUG ("unexpected check_rate error");
243     return FALSE;
244   }
245   gst_oss_helper_rate_add_range (ranges, probe->min + 1, probe->max - 1);
246
247   while ((range = g_queue_pop_head (ranges))) {
248     int min1;
249     int max1;
250     int mid;
251     int mid_ret;
252
253     GST_DEBUG ("checking [%d,%d]", range->min, range->max);
254
255     mid = (range->min + range->max) / 2;
256     mid_ret = gst_oss_helper_rate_check_rate (probe, mid);
257     if (mid_ret == -1) {
258       /* FIXME ioctl returned an error.  do something */
259       GST_DEBUG ("unexpected check_rate error");
260     }
261     n_checks++;
262
263     if (mid == mid_ret && checking_exact_rates) {
264       int max_exact_matches = 20;
265
266       exact_rates++;
267       if (exact_rates > max_exact_matches) {
268         GST_DEBUG ("got %d exact rates, assuming all are exact",
269             max_exact_matches);
270         result = FALSE;
271         g_free (range);
272         break;
273       }
274     } else {
275       checking_exact_rates = FALSE;
276     }
277
278     /* Assume that the rate is arithmetically rounded to the nearest
279      * supported rate. */
280     if (mid == mid_ret) {
281       min1 = mid - 1;
282       max1 = mid + 1;
283     } else {
284       if (mid < mid_ret) {
285         min1 = mid - (mid_ret - mid);
286         max1 = mid_ret + 1;
287       } else {
288         min1 = mid_ret - 1;
289         max1 = mid + (mid - mid_ret);
290       }
291     }
292
293     gst_oss_helper_rate_add_range (ranges, range->min, min1);
294     gst_oss_helper_rate_add_range (ranges, max1, range->max);
295
296     g_free (range);
297   }
298
299   while ((range = g_queue_pop_head (ranges))) {
300     g_free (range);
301   }
302   g_queue_free (ranges);
303
304   return result;
305 }
306
307 static void
308 gst_oss_helper_rate_add_range (GQueue * queue, int min, int max)
309 {
310   if (min <= max) {
311     GstOssRange *range = g_new0 (GstOssRange, 1);
312
313     range->min = min;
314     range->max = max;
315
316     g_queue_push_tail (queue, range);
317     /* push_head also works, but has different probing behavior */
318     /*g_queue_push_head (queue, range); */
319   }
320 }
321
322 static int
323 gst_oss_helper_rate_check_rate (GstOssProbe * probe, int irate)
324 {
325   int rate;
326   int format;
327   int n_channels;
328   int ret;
329
330   rate = irate;
331   format = probe->format;
332   n_channels = probe->n_channels;
333
334   GST_LOG ("checking format %d, channels %d, rate %d",
335       format, n_channels, rate);
336   ret = ioctl (probe->fd, SNDCTL_DSP_SETFMT, &format);
337   if (ret < 0 || format != probe->format) {
338     GST_DEBUG ("unsupported format: %d (%d)", probe->format, format);
339     return -1;
340   }
341   ret = ioctl (probe->fd, SNDCTL_DSP_CHANNELS, &n_channels);
342   if (ret < 0 || n_channels != probe->n_channels) {
343     GST_DEBUG ("unsupported channels: %d (%d)", probe->n_channels, n_channels);
344     return -1;
345   }
346   ret = ioctl (probe->fd, SNDCTL_DSP_SPEED, &rate);
347   if (ret < 0) {
348     GST_DEBUG ("unsupported rate: %d (%d)", irate, rate);
349     return -1;
350   }
351
352   GST_DEBUG ("rate %d -> %d", irate, rate);
353
354   if (rate == irate - 1 || rate == irate + 1) {
355     rate = irate;
356   }
357   gst_oss_helper_rate_add_rate (probe->rates, rate);
358   return rate;
359 }
360
361 static void
362 gst_oss_helper_rate_add_rate (GArray * array, int rate)
363 {
364   int i;
365   int val;
366
367   for (i = 0; i < array->len; i++) {
368     val = g_array_index (array, int, i);
369
370     if (val == rate)
371       return;
372   }
373   GST_DEBUG ("supported rate: %d", rate);
374   g_array_append_val (array, rate);
375 }
376
377 static int
378 gst_oss_helper_rate_int_compare (gconstpointer a, gconstpointer b)
379 {
380   const int *va = (const int *) a;
381   const int *vb = (const int *) b;
382
383   if (*va < *vb)
384     return -1;
385   if (*va > *vb)
386     return 1;
387   return 0;
388 }