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., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, 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 "gstosshelper.h"
51
52 GST_DEBUG_CATEGORY_EXTERN (oss_debug);
53 #define GST_CAT_DEFAULT oss_debug
54
55 typedef struct _GstOssProbe GstOssProbe;
56 struct _GstOssProbe
57 {
58 int fd;
59 int format;
60 int n_channels;
61 GArray *rates;
62 int min;
63 int max;
64 };
65
66 typedef struct _GstOssRange GstOssRange;
67 struct _GstOssRange
68 {
69 int min;
70 int max;
71 };
72
73 static GstStructure *gst_oss_helper_get_format_structure (unsigned int
74 format_bit);
75 static gboolean gst_oss_helper_rate_probe_check (GstOssProbe * probe);
76 static int gst_oss_helper_rate_check_rate (GstOssProbe * probe, int irate);
77 static void gst_oss_helper_rate_add_range (GQueue * queue, int min, int max);
78 static void gst_oss_helper_rate_add_rate (GArray * array, int rate);
79 static int gst_oss_helper_rate_int_compare (gconstpointer a, gconstpointer b);
80
81 GstCaps *
gst_oss_helper_probe_caps(gint fd)82 gst_oss_helper_probe_caps (gint fd)
83 {
84 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
85 const guint probe_formats[] = { AFMT_U32_LE, AFMT_S32_LE, AFMT_U24_LE, AFMT_S24_LE, AFMT_S16_LE, AFMT_U16_LE, AFMT_U8, AFMT_S8 };
86 #else
87 const guint probe_formats[] = { AFMT_U32_BE, AFMT_S32_BE, AFMT_U24_BE, AFMT_S24_BE, AFMT_S16_BE, AFMT_U16_BE, AFMT_U8, AFMT_S8 };
88 #endif
89 GstOssProbe *probe;
90 int i, f;
91 gboolean ret;
92 GstStructure *structure;
93 GstCaps *caps;
94
95 /* FIXME test make sure we're not currently playing */
96 /* FIXME test both mono and stereo */
97
98 caps = gst_caps_new_empty ();
99
100 /* assume that the most significant bit of format_mask is 0 */
101 for (f = 0; f < G_N_ELEMENTS (probe_formats); ++f) {
102 GValue rate_value = { 0 };
103
104 probe = g_new0 (GstOssProbe, 1);
105 probe->fd = fd;
106 probe->format = probe_formats[f];
107 /* FIXME: this is not working for all cards, see bug #518474 */
108 probe->n_channels = 2;
109
110 ret = gst_oss_helper_rate_probe_check (probe);
111 if (probe->min == -1 || probe->max == -1) {
112 g_array_free (probe->rates, TRUE);
113 g_free (probe);
114 continue;
115 }
116
117 if (ret) {
118 GValue value = { 0 };
119
120 g_array_sort (probe->rates, gst_oss_helper_rate_int_compare);
121
122 g_value_init (&rate_value, GST_TYPE_LIST);
123 g_value_init (&value, G_TYPE_INT);
124
125 for (i = 0; i < probe->rates->len; i++) {
126 g_value_set_int (&value, g_array_index (probe->rates, int, i));
127
128 gst_value_list_append_value (&rate_value, &value);
129 }
130
131 g_value_unset (&value);
132 } else {
133 /* one big range */
134 g_value_init (&rate_value, GST_TYPE_INT_RANGE);
135 gst_value_set_int_range (&rate_value, probe->min, probe->max);
136 }
137
138 g_array_free (probe->rates, TRUE);
139 g_free (probe);
140
141 structure = gst_oss_helper_get_format_structure (probe_formats[f]);
142 gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
143 gst_structure_set_value (structure, "rate", &rate_value);
144 g_value_unset (&rate_value);
145
146 gst_caps_append_structure (caps, structure);
147 }
148
149 if (gst_caps_is_empty (caps)) {
150 /* fixme: make user-visible */
151 GST_WARNING ("Your OSS device could not be probed correctly");
152 } else {
153 caps = gst_caps_simplify (caps);
154 }
155
156 GST_DEBUG ("probed caps: %" GST_PTR_FORMAT, caps);
157
158 return caps;
159 }
160
161 static GstStructure *
gst_oss_helper_get_format_structure(unsigned int format_bit)162 gst_oss_helper_get_format_structure (unsigned int format_bit)
163 {
164 GstStructure *structure;
165 const gchar *format;
166
167 switch (format_bit) {
168 case AFMT_S8:
169 format = "S8";
170 break;
171 case AFMT_U8:
172 format = "U8";
173 break;
174 case AFMT_S16_LE:
175 format = "S16LE";
176 break;
177 case AFMT_S16_BE:
178 format = "S16BE";
179 break;
180 case AFMT_U16_LE:
181 format = "U16LE";
182 break;
183 case AFMT_U16_BE:
184 format = "U16BE";
185 break;
186 case AFMT_S24_LE:
187 format = "S24LE";
188 break;
189 case AFMT_S24_BE:
190 format = "S24BE";
191 break;
192 case AFMT_U24_LE:
193 format = "U24LE";
194 break;
195 case AFMT_U24_BE:
196 format = "U24BE";
197 break;
198 case AFMT_S32_LE:
199 format = "S32LE";
200 break;
201 case AFMT_S32_BE:
202 format = "S32BE";
203 break;
204 case AFMT_U32_LE:
205 format = "U32LE";
206 break;
207 case AFMT_U32_BE:
208 format = "U32BE";
209 break;
210 default:
211 g_assert_not_reached ();
212 return NULL;
213 }
214
215 structure = gst_structure_new ("audio/x-raw",
216 "format", G_TYPE_STRING, format,
217 "layout", G_TYPE_STRING, "interleaved", NULL);
218
219 return structure;
220 }
221
222 static gboolean
gst_oss_helper_rate_probe_check(GstOssProbe * probe)223 gst_oss_helper_rate_probe_check (GstOssProbe * probe)
224 {
225 GstOssRange *range;
226 GQueue *ranges;
227 int exact_rates = 0;
228 gboolean checking_exact_rates = TRUE;
229 int n_checks = 0;
230 gboolean result = TRUE;
231
232 ranges = g_queue_new ();
233
234 probe->rates = g_array_new (FALSE, FALSE, sizeof (int));
235
236 probe->min = gst_oss_helper_rate_check_rate (probe, 1000);
237 n_checks++;
238 probe->max = gst_oss_helper_rate_check_rate (probe, 100000);
239 /* a little bug workaround */
240 {
241 int max;
242
243 max = gst_oss_helper_rate_check_rate (probe, 48000);
244 if (max > probe->max) {
245 GST_ERROR
246 ("Driver bug recognized (driver does not round rates correctly). Please file a bug report.");
247 probe->max = max;
248 }
249 }
250 n_checks++;
251 if (probe->min == -1 || probe->max == -1) {
252 /* This is a workaround for drivers that return -EINVAL (or another
253 * error) for rates outside of [8000,48000]. If this fails, the
254 * driver is seriously buggy, and probably doesn't work with other
255 * media libraries/apps. */
256 probe->min = gst_oss_helper_rate_check_rate (probe, 8000);
257 probe->max = gst_oss_helper_rate_check_rate (probe, 48000);
258 }
259 if (probe->min == -1 || probe->max == -1) {
260 GST_DEBUG ("unexpected check_rate error");
261 return FALSE;
262 }
263 gst_oss_helper_rate_add_range (ranges, probe->min + 1, probe->max - 1);
264
265 while ((range = g_queue_pop_head (ranges))) {
266 int min1;
267 int max1;
268 int mid;
269 int mid_ret;
270
271 GST_DEBUG ("checking [%d,%d]", range->min, range->max);
272
273 mid = (range->min + range->max) / 2;
274 mid_ret = gst_oss_helper_rate_check_rate (probe, mid);
275 if (mid_ret == -1) {
276 /* FIXME ioctl returned an error. do something */
277 GST_DEBUG ("unexpected check_rate error");
278 }
279 n_checks++;
280
281 if (mid == mid_ret && checking_exact_rates) {
282 int max_exact_matches = 20;
283
284 exact_rates++;
285 if (exact_rates > max_exact_matches) {
286 GST_DEBUG ("got %d exact rates, assuming all are exact",
287 max_exact_matches);
288 result = FALSE;
289 g_free (range);
290 break;
291 }
292 } else {
293 checking_exact_rates = FALSE;
294 }
295
296 /* Assume that the rate is arithmetically rounded to the nearest
297 * supported rate. */
298 if (mid == mid_ret) {
299 min1 = mid - 1;
300 max1 = mid + 1;
301 } else {
302 if (mid < mid_ret) {
303 min1 = mid - (mid_ret - mid);
304 max1 = mid_ret + 1;
305 } else {
306 min1 = mid_ret - 1;
307 max1 = mid + (mid - mid_ret);
308 }
309 }
310
311 gst_oss_helper_rate_add_range (ranges, range->min, min1);
312 gst_oss_helper_rate_add_range (ranges, max1, range->max);
313
314 g_free (range);
315 }
316
317 while ((range = g_queue_pop_head (ranges))) {
318 g_free (range);
319 }
320 g_queue_free (ranges);
321
322 return result;
323 }
324
325 static void
gst_oss_helper_rate_add_range(GQueue * queue,int min,int max)326 gst_oss_helper_rate_add_range (GQueue * queue, int min, int max)
327 {
328 if (min <= max) {
329 GstOssRange *range = g_new0 (GstOssRange, 1);
330
331 range->min = min;
332 range->max = max;
333
334 g_queue_push_tail (queue, range);
335 /* push_head also works, but has different probing behavior */
336 /*g_queue_push_head (queue, range); */
337 }
338 }
339
340 static int
gst_oss_helper_rate_check_rate(GstOssProbe * probe,int irate)341 gst_oss_helper_rate_check_rate (GstOssProbe * probe, int irate)
342 {
343 int rate;
344 int format;
345 int n_channels;
346 int ret;
347 int rst;
348
349 rate = irate;
350 format = probe->format;
351 n_channels = probe->n_channels;
352 rst = 4000; /* XXX Lowest supported rate for FreeBSD. */
353
354 GST_LOG ("checking format %d, channels %d, rate %d",
355 format, n_channels, rate);
356 /* Reset rate to lowest supported rate. */
357 ioctl (probe->fd, SNDCTL_DSP_SPEED, &rst);
358 ret = ioctl (probe->fd, SNDCTL_DSP_SETFMT, &format);
359 if (ret < 0 || format != probe->format) {
360 GST_DEBUG ("unsupported format: %d (%d)", probe->format, format);
361 return -1;
362 }
363 ret = ioctl (probe->fd, SNDCTL_DSP_CHANNELS, &n_channels);
364 if (ret < 0 || n_channels != probe->n_channels) {
365 GST_DEBUG ("unsupported channels: %d (%d)", probe->n_channels, n_channels);
366 return -1;
367 }
368 ret = ioctl (probe->fd, SNDCTL_DSP_SPEED, &rate);
369 if (ret < 0) {
370 GST_DEBUG ("unsupported rate: %d (%d)", irate, rate);
371 return -1;
372 }
373
374 GST_DEBUG ("rate %d -> %d", irate, rate);
375
376 if (rate == irate - 1 || rate == irate + 1) {
377 rate = irate;
378 }
379 gst_oss_helper_rate_add_rate (probe->rates, rate);
380 return rate;
381 }
382
383 static void
gst_oss_helper_rate_add_rate(GArray * array,int rate)384 gst_oss_helper_rate_add_rate (GArray * array, int rate)
385 {
386 int i;
387 int val;
388
389 for (i = 0; i < array->len; i++) {
390 val = g_array_index (array, int, i);
391
392 if (val == rate)
393 return;
394 }
395 GST_DEBUG ("supported rate: %d", rate);
396 g_array_append_val (array, rate);
397 }
398
399 static int
gst_oss_helper_rate_int_compare(gconstpointer a,gconstpointer b)400 gst_oss_helper_rate_int_compare (gconstpointer a, gconstpointer b)
401 {
402 const int *va = (const int *) a;
403 const int *vb = (const int *) b;
404
405 if (*va < *vb)
406 return -1;
407 if (*va > *vb)
408 return 1;
409 return 0;
410 }
411
412 gchar *
gst_oss_helper_get_card_name(const gchar * mixer_name)413 gst_oss_helper_get_card_name (const gchar * mixer_name)
414 {
415 #ifdef SOUND_MIXER_INFO
416 struct mixer_info minfo;
417 #endif
418 gint fd;
419 gchar *name = NULL;
420
421 GST_INFO ("Opening mixer for device %s", mixer_name);
422 fd = open (mixer_name, O_RDWR);
423 if (fd == -1)
424 goto open_failed;
425
426 /* get name, not fatal */
427 #ifdef SOUND_MIXER_INFO
428 if (ioctl (fd, SOUND_MIXER_INFO, &minfo) == 0) {
429 name = g_strdup (minfo.name);
430 GST_INFO ("Card name = %s", GST_STR_NULL (name));
431 } else
432 #endif
433 {
434 name = g_strdup ("Unknown");
435 GST_INFO ("Unknown card name");
436 }
437
438 close (fd);
439 return name;
440
441 /* ERRORS */
442 open_failed:
443 {
444 /* this is valid. OSS devices don't need to expose a mixer */
445 GST_DEBUG ("Failed to open mixer device %s, mixing disabled: %s",
446 mixer_name, strerror (errno));
447 return NULL;
448 }
449 }
450