1 /* GStreamer OSS4 audio property probe interface implementation
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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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 <gst/gst.h>
29
30
31 #define NO_LEGACY_MIXER
32 #include "oss4-audio.h"
33 #include "oss4-sink.h"
34 #include "oss4-source.h"
35 #include "oss4-soundcard.h"
36 #include "oss4-property-probe.h"
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/ioctl.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <string.h>
45
46 #if 0
47
48 GST_DEBUG_CATEGORY_EXTERN (oss4_debug);
49 #define GST_CAT_DEFAULT oss4_debug
50
51 static const GList *
52 gst_oss4_property_probe_get_properties (GstPropertyProbe * probe)
53 {
54 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
55 GList *list;
56
57 GST_OBJECT_LOCK (GST_OBJECT (probe));
58
59 /* we create a new list and store it in the instance struct, since apparently
60 * we forgot to update the API for 0.10 (and why don't we mark probable
61 * properties with a flag instead anyway?). A bit hackish, but what can you
62 * do (can't really use a static variable since the pspec will be different
63 * for src and sink class); this isn't particularly pretty, but the best
64 * we can do given that we can't create a common base class (we could do
65 * fancy things with the interface, or use g_object_set_data instead, but
66 * it's not really going to make it much better) */
67 if (GST_IS_AUDIO_SINK_CLASS (klass)) {
68 list = GST_OSS4_SINK (probe)->property_probe_list;
69 } else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
70 list = GST_OSS4_SOURCE (probe)->property_probe_list;
71 } else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
72 list = GST_OSS4_MIXER (probe)->property_probe_list;
73 } else {
74 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
75 g_return_val_if_reached (NULL);
76 }
77
78 if (list == NULL) {
79 GParamSpec *pspec;
80
81 pspec = g_object_class_find_property (klass, "device");
82 list = g_list_prepend (NULL, pspec);
83
84 if (GST_IS_AUDIO_SINK_CLASS (klass)) {
85 GST_OSS4_SINK (probe)->property_probe_list = list;
86 } else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
87 GST_OSS4_SOURCE (probe)->property_probe_list = list;
88 } else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
89 GST_OSS4_MIXER (probe)->property_probe_list = list;
90 }
91 }
92
93 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
94
95 return list;
96 }
97
98 static void
99 gst_oss4_property_probe_probe_property (GstPropertyProbe * probe,
100 guint prop_id, const GParamSpec * pspec)
101 {
102 if (!g_str_equal (pspec->name, "device")) {
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
104 }
105 }
106
107 static gboolean
108 gst_oss4_property_probe_needs_probe (GstPropertyProbe * probe,
109 guint prop_id, const GParamSpec * pspec)
110 {
111 /* don't cache probed data */
112 return TRUE;
113 }
114
115 #endif
116
117 /* caller must ensure LOCK is taken (e.g. if ioctls need to be serialised) */
118 gboolean
gst_oss4_property_probe_find_device_name(GstObject * obj,int fd,const gchar * device_handle,gchar ** device_name)119 gst_oss4_property_probe_find_device_name (GstObject * obj, int fd,
120 const gchar * device_handle, gchar ** device_name)
121 {
122 struct oss_sysinfo si = { {0,}, };
123 gchar *name = NULL;
124
125 if (ioctl (fd, SNDCTL_SYSINFO, &si) == 0) {
126 int i;
127
128 for (i = 0; i < si.numaudios; ++i) {
129 struct oss_audioinfo ai = { 0, };
130
131 ai.dev = i;
132 if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
133 GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
134 continue;
135 }
136 if (strcmp (ai.devnode, device_handle) == 0) {
137 name = g_strdup (ai.name);
138 break;
139 }
140 }
141 } else {
142 GST_WARNING_OBJECT (obj, "SYSINFO ioctl failed: %s", g_strerror (errno));
143 }
144
145 /* try ENGINEINFO as fallback (which is better than nothing) */
146 if (name == NULL) {
147 struct oss_audioinfo ai = { 0, };
148
149 GST_LOG_OBJECT (obj, "device %s not listed in AUDIOINFO", device_handle);
150 ai.dev = -1;
151 if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == 0)
152 name = g_strdup (ai.name);
153 }
154
155 GST_DEBUG_OBJECT (obj, "Device name: %s", GST_STR_NULL (name));
156
157 if (name != NULL)
158 *device_name = name;
159
160 return (name != NULL);
161 }
162
163 gboolean
gst_oss4_property_probe_find_device_name_nofd(GstObject * obj,const gchar * device_handle,gchar ** device_name)164 gst_oss4_property_probe_find_device_name_nofd (GstObject * obj,
165 const gchar * device_handle, gchar ** device_name)
166 {
167 gboolean res;
168 int fd;
169
170 fd = open ("/dev/mixer", O_RDONLY);
171 if (fd < 0)
172 return FALSE;
173
174 res = gst_oss4_property_probe_find_device_name (obj, fd, device_handle,
175 device_name);
176
177 close (fd);
178 return res;
179 }
180
181 static GList *
gst_oss4_property_probe_get_audio_devices(GstObject * obj,int fd,struct oss_sysinfo * si,int cap_mask)182 gst_oss4_property_probe_get_audio_devices (GstObject * obj, int fd,
183 struct oss_sysinfo *si, int cap_mask)
184 {
185 GList *devices = NULL;
186 int i;
187
188 GST_LOG_OBJECT (obj, "%d audio/dsp devices", si->numaudios);
189
190 for (i = 0; i < si->numaudios; ++i) {
191 struct oss_audioinfo ai = { 0, };
192
193 ai.dev = i;
194 if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
195 GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
196 continue;
197 }
198
199 if ((ai.caps & cap_mask) == 0) {
200 GST_DEBUG_OBJECT (obj, "audio device %d is not an %s device", i,
201 (cap_mask == PCM_CAP_OUTPUT) ? "output" : "input");
202 continue;
203 }
204
205 if (!ai.enabled) {
206 GST_DEBUG_OBJECT (obj, "audio device %d is not usable/enabled", i);
207 continue;
208 }
209
210 GST_DEBUG_OBJECT (obj, "audio device %d looks ok: %s (\"%s\")", i,
211 ai.devnode, ai.name);
212
213 devices = g_list_prepend (devices, g_strdup (ai.devnode));
214 }
215
216 return g_list_reverse (devices);
217 }
218
219 GValueArray *
gst_oss4_property_probe_get_values(GstObject * probe,const gchar * pname)220 gst_oss4_property_probe_get_values (GstObject * probe, const gchar * pname)
221 {
222 struct oss_sysinfo si = { {0,}, };
223 GValueArray *array = NULL;
224 GstObject *obj;
225 GList *devices, *l;
226 int cap_mask, fd = -1;
227
228 if (!g_str_equal (pname, "device")) {
229 GST_WARNING_OBJECT (probe, "invalid property");
230 return NULL;
231 }
232
233 obj = GST_OBJECT (probe);
234
235 GST_OBJECT_LOCK (obj);
236
237 /* figure out whether the element is a source or sink */
238 if (GST_IS_OSS4_SINK (probe)) {
239 GST_DEBUG_OBJECT (probe, "probing available output devices");
240 cap_mask = PCM_CAP_OUTPUT;
241 fd = GST_OSS4_SINK (probe)->fd;
242 } else if (GST_IS_OSS4_SOURCE (probe)) {
243 GST_DEBUG_OBJECT (probe, "probing available input devices");
244 cap_mask = PCM_CAP_INPUT;
245 fd = GST_OSS4_SOURCE (probe)->fd;
246 } else {
247 GST_OBJECT_UNLOCK (obj);
248 g_assert_not_reached ();
249 return NULL;
250 }
251
252 /* copy fd if it's open, so we can just unconditionally close() later */
253 if (fd != -1)
254 fd = dup (fd);
255
256 /* this will also catch the unlikely case where the above dup() failed */
257 if (fd == -1) {
258 fd = open ("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
259 if (fd < 0)
260 goto open_failed;
261 else if (!gst_oss4_audio_check_version (GST_OBJECT (probe), fd))
262 goto legacy_oss;
263 }
264
265 if (ioctl (fd, SNDCTL_SYSINFO, &si) == -1)
266 goto no_sysinfo;
267
268 devices = gst_oss4_property_probe_get_audio_devices (obj, fd, &si, cap_mask);
269
270 if (devices == NULL) {
271 GST_OBJECT_UNLOCK (obj);
272 GST_DEBUG_OBJECT (obj, "No devices found");
273 goto done;
274 }
275
276 array = g_value_array_new (1);
277
278 for (l = devices; l != NULL; l = l->next) {
279 GValue val = { 0, };
280
281 g_value_init (&val, G_TYPE_STRING);
282 g_value_take_string (&val, (gchar *) l->data);
283 l->data = NULL;
284 g_value_array_append (array, &val);
285 g_value_unset (&val);
286 }
287
288 GST_OBJECT_UNLOCK (obj);
289
290 g_list_free (devices);
291
292 done:
293
294 close (fd);
295
296 return array;
297
298 /* ERRORS */
299 open_failed:
300 {
301 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
302 GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
303 "available devices: %s", g_strerror (errno));
304 return NULL;
305 }
306 legacy_oss:
307 {
308 close (fd);
309 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
310 GST_DEBUG_OBJECT (probe, "Legacy OSS (ie. not OSSv4), not supported");
311 return NULL;
312 }
313 no_sysinfo:
314 {
315 close (fd);
316 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
317 GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
318 "available devices: %s", g_strerror (errno));
319 return NULL;
320 }
321 }
322
323 #if 0
324 static void
325 gst_oss4_property_probe_interface_init (GstPropertyProbeInterface * iface)
326 {
327 iface->get_properties = gst_oss4_property_probe_get_properties;
328 iface->probe_property = gst_oss4_property_probe_probe_property;
329 iface->needs_probe = gst_oss4_property_probe_needs_probe;
330 iface->get_values = gst_oss4_property_probe_get_values;
331 }
332
333 void
334 gst_oss4_add_property_probe_interface (GType type)
335 {
336 static const GInterfaceInfo probe_iface_info = {
337 (GInterfaceInitFunc) gst_oss4_property_probe_interface_init,
338 NULL,
339 NULL,
340 };
341
342 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
343 &probe_iface_info);
344 }
345 #endif
346