1 /* GStreamer OSS4 audio property probe interface implementation
2 * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
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.
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.
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.
26 #define NO_LEGACY_MIXER
27 #include "oss4-audio.h"
28 #include "oss4-mixer.h"
29 #include "oss4-sink.h"
30 #include "oss4-source.h"
31 #include "oss4-soundcard.h"
32 #include "oss4-property-probe.h"
34 #include <sys/types.h>
36 #include <sys/ioctl.h>
42 GST_DEBUG_CATEGORY_EXTERN (oss4_debug);
43 #define GST_CAT_DEFAULT oss4_debug
46 gst_oss4_property_probe_get_properties (GstPropertyProbe * probe)
48 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
51 GST_OBJECT_LOCK (GST_OBJECT (probe));
53 /* we create a new list and store it in the instance struct, since apparently
54 * we forgot to update the API for 0.10 (and why don't we mark probable
55 * properties with a flag instead anyway?). A bit hackish, but what can you
56 * do (can't really use a static variable since the pspec will be different
57 * for src and sink class); this isn't particularly pretty, but the best
58 * we can do given that we can't create a common base class (we could do
59 * fancy things with the interface, or use g_object_set_data instead, but
60 * it's not really going to make it much better) */
61 if (GST_IS_AUDIO_SINK_CLASS (klass)) {
62 list = GST_OSS4_SINK (probe)->property_probe_list;
63 } else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
64 list = GST_OSS4_SOURCE (probe)->property_probe_list;
65 } else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
66 list = GST_OSS4_MIXER (probe)->property_probe_list;
68 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
69 g_return_val_if_reached (NULL);
75 pspec = g_object_class_find_property (klass, "device");
76 list = g_list_prepend (NULL, pspec);
78 if (GST_IS_AUDIO_SINK_CLASS (klass)) {
79 GST_OSS4_SINK (probe)->property_probe_list = list;
80 } else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
81 GST_OSS4_SOURCE (probe)->property_probe_list = list;
82 } else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
83 GST_OSS4_MIXER (probe)->property_probe_list = list;
87 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
93 gst_oss4_property_probe_probe_property (GstPropertyProbe * probe,
94 guint prop_id, const GParamSpec * pspec)
96 if (!g_str_equal (pspec->name, "device")) {
97 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
102 gst_oss4_property_probe_needs_probe (GstPropertyProbe * probe,
103 guint prop_id, const GParamSpec * pspec)
105 /* don't cache probed data */
110 oss4_mixerinfo_priority_cmp (struct oss_mixerinfo *mi1,
111 struct oss_mixerinfo *mi2)
113 /* return negative vaue if mi1 comes before mi2 */
114 if (mi1->priority != mi2->priority)
115 return mi2->priority - mi1->priority;
117 return strcmp (mi1->devnode, mi2->devnode);
120 /* caller must ensure LOCK is taken (e.g. if ioctls need to be serialised) */
122 gst_oss4_property_probe_find_device_name (GstObject * obj, int fd,
123 const gchar * device_handle, gchar ** device_name)
125 struct oss_sysinfo si = { {0,}, };
128 if (ioctl (fd, SNDCTL_SYSINFO, &si) == 0) {
131 for (i = 0; i < si.numaudios; ++i) {
132 struct oss_audioinfo ai = { 0, };
135 if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
136 GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
139 if (strcmp (ai.devnode, device_handle) == 0) {
140 name = g_strdup (ai.name);
145 GST_WARNING_OBJECT (obj, "SYSINFO ioctl failed: %s", g_strerror (errno));
148 /* try ENGINEINFO as fallback (which is better than nothing) */
150 struct oss_audioinfo ai = { 0, };
152 GST_LOG_OBJECT (obj, "device %s not listed in AUDIOINFO", device_handle);
154 if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == 0)
155 name = g_strdup (ai.name);
158 GST_DEBUG_OBJECT (obj, "Device name: %s", GST_STR_NULL (name));
163 return (name != NULL);
167 gst_oss4_property_probe_find_device_name_nofd (GstObject * obj,
168 const gchar * device_handle, gchar ** device_name)
173 fd = open ("/dev/mixer", O_RDONLY);
177 res = gst_oss4_property_probe_find_device_name (obj, fd, device_handle,
185 gst_oss4_property_probe_get_mixer_devices (GstObject * obj, int fd,
186 struct oss_sysinfo *si)
188 GList *m, *mixers = NULL;
189 GList *devices = NULL;
193 GST_LOG_OBJECT (obj, "%d mixer devices", si->nummixers);
195 /* first, find suitable mixer devices and sort by priority */
196 for (i = 0; i < si->nummixers; ++i) {
197 struct oss_mixerinfo mi = { 0, };
200 if (ioctl (fd, SNDCTL_MIXERINFO, &mi) == -1) {
201 GST_DEBUG_OBJECT (obj, "MIXERINFO ioctl for device %d failed", i);
205 GST_INFO_OBJECT (obj, "mixer device %d:", i);
206 GST_INFO_OBJECT (obj, " enabled : %s", (mi.enabled) ? "yes" :
207 "no (powered off or unplugged)");
208 GST_INFO_OBJECT (obj, " priority : %d", mi.priority);
209 GST_INFO_OBJECT (obj, " devnode : %s", mi.devnode);
210 GST_INFO_OBJECT (obj, " handle : %s", mi.handle);
211 GST_INFO_OBJECT (obj, " caps : 0x%02x", mi.caps);
212 GST_INFO_OBJECT (obj, " name : %s", mi.name);
215 GST_DEBUG_OBJECT (obj, "mixer device is not usable/enabled, skipping");
219 /* only want mixers that control hardware directly */
220 if ((mi.caps & MIXER_CAP_VIRTUAL)) {
221 GST_DEBUG_OBJECT (obj, "mixer device is a virtual device, skipping");
225 mixers = g_list_insert_sorted (mixers, g_memdup (&mi, sizeof (mi)),
226 (GCompareFunc) oss4_mixerinfo_priority_cmp);
229 /* then create device list according to priority */
230 for (m = mixers; m != NULL; m = m->next) {
231 struct oss_mixerinfo *mi = (struct oss_mixerinfo *) m->data;
233 GST_LOG_OBJECT (obj, "mixer device: '%s'", mi->devnode);
234 devices = g_list_prepend (devices, g_strdup (mi->devnode));
237 g_list_free (mixers);
240 return g_list_reverse (devices);
244 gst_oss4_property_probe_get_audio_devices (GstObject * obj, int fd,
245 struct oss_sysinfo *si, int cap_mask)
247 GList *devices = NULL;
250 GST_LOG_OBJECT (obj, "%d audio/dsp devices", si->numaudios);
252 for (i = 0; i < si->numaudios; ++i) {
253 struct oss_audioinfo ai = { 0, };
256 if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
257 GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
261 if ((ai.caps & cap_mask) == 0) {
262 GST_DEBUG_OBJECT (obj, "audio device %d is not an %s device", i,
263 (cap_mask == PCM_CAP_OUTPUT) ? "output" : "input");
268 GST_DEBUG_OBJECT (obj, "audio device %d is not usable/enabled", i);
272 GST_DEBUG_OBJECT (obj, "audio device %d looks ok: %s (\"%s\")", i,
273 ai.devnode, ai.name);
275 devices = g_list_prepend (devices, g_strdup (ai.devnode));
278 return g_list_reverse (devices);
282 gst_oss4_property_probe_get_values (GstPropertyProbe * probe,
283 guint prop_id, const GParamSpec * pspec)
285 struct oss_sysinfo si = { {0,}, };
286 GValueArray *array = NULL;
289 int cap_mask, fd = -1;
291 if (!g_str_equal (pspec->name, "device")) {
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
296 obj = GST_OBJECT (probe);
298 GST_OBJECT_LOCK (obj);
300 /* figure out whether the element is a source or sink */
301 if (GST_IS_OSS4_SINK (probe)) {
302 GST_DEBUG_OBJECT (probe, "probing available output devices");
303 cap_mask = PCM_CAP_OUTPUT;
304 fd = GST_OSS4_SINK (probe)->fd;
305 } else if (GST_IS_OSS4_SOURCE (probe)) {
306 GST_DEBUG_OBJECT (probe, "probing available input devices");
307 cap_mask = PCM_CAP_INPUT;
308 fd = GST_OSS4_SOURCE (probe)->fd;
309 } else if (GST_IS_OSS4_MIXER (probe)) {
310 fd = GST_OSS4_MIXER (probe)->fd;
313 GST_OBJECT_UNLOCK (obj);
314 g_return_val_if_reached (NULL);
317 /* copy fd if it's open, so we can just unconditionally close() later */
321 /* this will also catch the unlikely case where the above dup() failed */
323 fd = open ("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
326 else if (!gst_oss4_audio_check_version (GST_OBJECT (probe), fd))
330 if (ioctl (fd, SNDCTL_SYSINFO, &si) == -1)
335 gst_oss4_property_probe_get_audio_devices (obj, fd, &si, cap_mask);
337 devices = gst_oss4_property_probe_get_mixer_devices (obj, fd, &si);
340 if (devices == NULL) {
341 GST_DEBUG_OBJECT (obj, "No devices found");
345 array = g_value_array_new (1);
347 for (l = devices; l != NULL; l = l->next) {
350 g_value_init (&val, G_TYPE_STRING);
351 g_value_take_string (&val, (gchar *) l->data);
353 g_value_array_append (array, &val);
354 g_value_unset (&val);
357 GST_OBJECT_UNLOCK (obj);
359 g_list_free (devices);
370 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
371 GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
372 "available devices: %s", g_strerror (errno));
378 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
379 GST_DEBUG_OBJECT (probe, "Legacy OSS (ie. not OSSv4), not supported");
385 GST_OBJECT_UNLOCK (GST_OBJECT (probe));
386 GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
387 "available devices: %s", g_strerror (errno));
393 gst_oss4_property_probe_interface_init (GstPropertyProbeInterface * iface)
395 iface->get_properties = gst_oss4_property_probe_get_properties;
396 iface->probe_property = gst_oss4_property_probe_probe_property;
397 iface->needs_probe = gst_oss4_property_probe_needs_probe;
398 iface->get_values = gst_oss4_property_probe_get_values;
402 gst_oss4_add_property_probe_interface (GType type)
404 static const GInterfaceInfo probe_iface_info = {
405 (GInterfaceInitFunc) gst_oss4_property_probe_interface_init,
410 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,