diff options
Diffstat (limited to 'sys/oss4/oss4-property-probe.c')
-rwxr-xr-x | sys/oss4/oss4-property-probe.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/sys/oss4/oss4-property-probe.c b/sys/oss4/oss4-property-probe.c new file mode 100755 index 0000000..4644ea1 --- /dev/null +++ b/sys/oss4/oss4-property-probe.c @@ -0,0 +1,345 @@ +/* GStreamer OSS4 audio property probe interface implementation + * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gst/gst.h> + + +#define NO_LEGACY_MIXER +#include "oss4-audio.h" +#include "oss4-sink.h" +#include "oss4-source.h" +#include "oss4-soundcard.h" +#include "oss4-property-probe.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#if 0 + +GST_DEBUG_CATEGORY_EXTERN (oss4_debug); +#define GST_CAT_DEFAULT oss4_debug + +static const GList * +gst_oss4_property_probe_get_properties (GstPropertyProbe * probe) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (probe); + GList *list; + + GST_OBJECT_LOCK (GST_OBJECT (probe)); + + /* we create a new list and store it in the instance struct, since apparently + * we forgot to update the API for 0.10 (and why don't we mark probable + * properties with a flag instead anyway?). A bit hackish, but what can you + * do (can't really use a static variable since the pspec will be different + * for src and sink class); this isn't particularly pretty, but the best + * we can do given that we can't create a common base class (we could do + * fancy things with the interface, or use g_object_set_data instead, but + * it's not really going to make it much better) */ + if (GST_IS_AUDIO_SINK_CLASS (klass)) { + list = GST_OSS4_SINK (probe)->property_probe_list; + } else if (GST_IS_AUDIO_SRC_CLASS (klass)) { + list = GST_OSS4_SOURCE (probe)->property_probe_list; + } else if (GST_IS_OSS4_MIXER_CLASS (klass)) { + list = GST_OSS4_MIXER (probe)->property_probe_list; + } else { + GST_OBJECT_UNLOCK (GST_OBJECT (probe)); + g_return_val_if_reached (NULL); + } + + if (list == NULL) { + GParamSpec *pspec; + + pspec = g_object_class_find_property (klass, "device"); + list = g_list_prepend (NULL, pspec); + + if (GST_IS_AUDIO_SINK_CLASS (klass)) { + GST_OSS4_SINK (probe)->property_probe_list = list; + } else if (GST_IS_AUDIO_SRC_CLASS (klass)) { + GST_OSS4_SOURCE (probe)->property_probe_list = list; + } else if (GST_IS_OSS4_MIXER_CLASS (klass)) { + GST_OSS4_MIXER (probe)->property_probe_list = list; + } + } + + GST_OBJECT_UNLOCK (GST_OBJECT (probe)); + + return list; +} + +static void +gst_oss4_property_probe_probe_property (GstPropertyProbe * probe, + guint prop_id, const GParamSpec * pspec) +{ + if (!g_str_equal (pspec->name, "device")) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); + } +} + +static gboolean +gst_oss4_property_probe_needs_probe (GstPropertyProbe * probe, + guint prop_id, const GParamSpec * pspec) +{ + /* don't cache probed data */ + return TRUE; +} + +#endif + +/* caller must ensure LOCK is taken (e.g. if ioctls need to be serialised) */ +gboolean +gst_oss4_property_probe_find_device_name (GstObject * obj, int fd, + const gchar * device_handle, gchar ** device_name) +{ + struct oss_sysinfo si = { {0,}, }; + gchar *name = NULL; + + if (ioctl (fd, SNDCTL_SYSINFO, &si) == 0) { + int i; + + for (i = 0; i < si.numaudios; ++i) { + struct oss_audioinfo ai = { 0, }; + + ai.dev = i; + if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) { + GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i); + continue; + } + if (strcmp (ai.devnode, device_handle) == 0) { + name = g_strdup (ai.name); + break; + } + } + } else { + GST_WARNING_OBJECT (obj, "SYSINFO ioctl failed: %s", g_strerror (errno)); + } + + /* try ENGINEINFO as fallback (which is better than nothing) */ + if (name == NULL) { + struct oss_audioinfo ai = { 0, }; + + GST_LOG_OBJECT (obj, "device %s not listed in AUDIOINFO", device_handle); + ai.dev = -1; + if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == 0) + name = g_strdup (ai.name); + } + + GST_DEBUG_OBJECT (obj, "Device name: %s", GST_STR_NULL (name)); + + if (name != NULL) + *device_name = name; + + return (name != NULL); +} + +gboolean +gst_oss4_property_probe_find_device_name_nofd (GstObject * obj, + const gchar * device_handle, gchar ** device_name) +{ + gboolean res; + int fd; + + fd = open ("/dev/mixer", O_RDONLY); + if (fd < 0) + return FALSE; + + res = gst_oss4_property_probe_find_device_name (obj, fd, device_handle, + device_name); + + close (fd); + return res; +} + +static GList * +gst_oss4_property_probe_get_audio_devices (GstObject * obj, int fd, + struct oss_sysinfo *si, int cap_mask) +{ + GList *devices = NULL; + int i; + + GST_LOG_OBJECT (obj, "%d audio/dsp devices", si->numaudios); + + for (i = 0; i < si->numaudios; ++i) { + struct oss_audioinfo ai = { 0, }; + + ai.dev = i; + if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) { + GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i); + continue; + } + + if ((ai.caps & cap_mask) == 0) { + GST_DEBUG_OBJECT (obj, "audio device %d is not an %s device", i, + (cap_mask == PCM_CAP_OUTPUT) ? "output" : "input"); + continue; + } + + if (!ai.enabled) { + GST_DEBUG_OBJECT (obj, "audio device %d is not usable/enabled", i); + continue; + } + + GST_DEBUG_OBJECT (obj, "audio device %d looks ok: %s (\"%s\")", i, + ai.devnode, ai.name); + + devices = g_list_prepend (devices, g_strdup (ai.devnode)); + } + + return g_list_reverse (devices); +} + +GValueArray * +gst_oss4_property_probe_get_values (GstObject * probe, const gchar * pname) +{ + struct oss_sysinfo si = { {0,}, }; + GValueArray *array = NULL; + GstObject *obj; + GList *devices, *l; + int cap_mask, fd = -1; + + if (!g_str_equal (pname, "device")) { + GST_WARNING_OBJECT (probe, "invalid property"); + return NULL; + } + + obj = GST_OBJECT (probe); + + GST_OBJECT_LOCK (obj); + + /* figure out whether the element is a source or sink */ + if (GST_IS_OSS4_SINK (probe)) { + GST_DEBUG_OBJECT (probe, "probing available output devices"); + cap_mask = PCM_CAP_OUTPUT; + fd = GST_OSS4_SINK (probe)->fd; + } else if (GST_IS_OSS4_SOURCE (probe)) { + GST_DEBUG_OBJECT (probe, "probing available input devices"); + cap_mask = PCM_CAP_INPUT; + fd = GST_OSS4_SOURCE (probe)->fd; + } else { + GST_OBJECT_UNLOCK (obj); + g_assert_not_reached (); + return NULL; + } + + /* copy fd if it's open, so we can just unconditionally close() later */ + if (fd != -1) + fd = dup (fd); + + /* this will also catch the unlikely case where the above dup() failed */ + if (fd == -1) { + fd = open ("/dev/mixer", O_RDONLY | O_NONBLOCK, 0); + if (fd < 0) + goto open_failed; + else if (!gst_oss4_audio_check_version (GST_OBJECT (probe), fd)) + goto legacy_oss; + } + + if (ioctl (fd, SNDCTL_SYSINFO, &si) == -1) + goto no_sysinfo; + + devices = gst_oss4_property_probe_get_audio_devices (obj, fd, &si, cap_mask); + + if (devices == NULL) { + GST_OBJECT_UNLOCK (obj); + GST_DEBUG_OBJECT (obj, "No devices found"); + goto done; + } + + array = g_value_array_new (1); + + for (l = devices; l != NULL; l = l->next) { + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_STRING); + g_value_take_string (&val, (gchar *) l->data); + l->data = NULL; + g_value_array_append (array, &val); + g_value_unset (&val); + } + + GST_OBJECT_UNLOCK (obj); + + g_list_free (devices); + +done: + + close (fd); + + return array; + +/* ERRORS */ +open_failed: + { + GST_OBJECT_UNLOCK (GST_OBJECT (probe)); + GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe " + "available devices: %s", g_strerror (errno)); + return NULL; + } +legacy_oss: + { + close (fd); + GST_OBJECT_UNLOCK (GST_OBJECT (probe)); + GST_DEBUG_OBJECT (probe, "Legacy OSS (ie. not OSSv4), not supported"); + return NULL; + } +no_sysinfo: + { + close (fd); + GST_OBJECT_UNLOCK (GST_OBJECT (probe)); + GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe " + "available devices: %s", g_strerror (errno)); + return NULL; + } +} + +#if 0 +static void +gst_oss4_property_probe_interface_init (GstPropertyProbeInterface * iface) +{ + iface->get_properties = gst_oss4_property_probe_get_properties; + iface->probe_property = gst_oss4_property_probe_probe_property; + iface->needs_probe = gst_oss4_property_probe_needs_probe; + iface->get_values = gst_oss4_property_probe_get_values; +} + +void +gst_oss4_add_property_probe_interface (GType type) +{ + static const GInterfaceInfo probe_iface_info = { + (GInterfaceInitFunc) gst_oss4_property_probe_interface_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, + &probe_iface_info); +} +#endif |