summaryrefslogtreecommitdiffstats
path: root/sys/oss4/oss4-property-probe.c
diff options
context:
space:
mode:
authorTim-Philipp Müller <tim@centricular.net>2008-04-02 20:18:58 +0000
committerTim-Philipp Müller <tim@centricular.net>2008-04-02 20:18:58 +0000
commita4246ff3a994d8ab64841f70b698917e39836bd2 (patch)
tree6b59a6b5aee78ca6d9dfd42133b0f8576b6a5787 /sys/oss4/oss4-property-probe.c
parent3e89814bb10346cabef1a836e8f0dc069f02371c (diff)
downloadgst-plugins-bad-a4246ff3a994d8ab64841f70b698917e39836bd2.tar.gz
gst-plugins-bad-a4246ff3a994d8ab64841f70b698917e39836bd2.tar.bz2
gst-plugins-bad-a4246ff3a994d8ab64841f70b698917e39836bd2.zip
Add initial support for OSSv4. Mixer still needs a bit more love, but even magic has its limits.
Original commit message from CVS: * configure.ac: * sys/Makefile.am: * sys/oss4/Makefile.am: * sys/oss4/oss4-audio.c: * sys/oss4/oss4-audio.h: * sys/oss4/oss4-mixer-enum.c: * sys/oss4/oss4-mixer-enum.h: * sys/oss4/oss4-mixer-slider.c: * sys/oss4/oss4-mixer-slider.h: * sys/oss4/oss4-mixer-switch.c: * sys/oss4/oss4-mixer-switch.h: * sys/oss4/oss4-mixer.c: * sys/oss4/oss4-mixer.h: * sys/oss4/oss4-property-probe.c: * sys/oss4/oss4-property-probe.h: * sys/oss4/oss4-sink.c: * sys/oss4/oss4-sink.h: * sys/oss4/oss4-soundcard.h: * sys/oss4/oss4-source.c: * sys/oss4/oss4-source.h: Add initial support for OSSv4. Mixer still needs a bit more love, but even magic has its limits.
Diffstat (limited to 'sys/oss4/oss4-property-probe.c')
-rw-r--r--sys/oss4/oss4-property-probe.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/sys/oss4/oss4-property-probe.c b/sys/oss4/oss4-property-probe.c
new file mode 100644
index 00000000..e4b56679
--- /dev/null
+++ b/sys/oss4/oss4-property-probe.c
@@ -0,0 +1,396 @@
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/gst.h>
+
+#define NO_LEGACY_MIXER
+#include "oss4-audio.h"
+#include "oss4-mixer.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>
+
+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;
+}
+
+static gint
+oss4_mixerinfo_priority_cmp (struct oss_mixerinfo *mi1,
+ struct oss_mixerinfo *mi2)
+{
+ /* return negative vaue if mi1 comes before mi2 */
+ if (mi1->priority != mi2->priority)
+ return mi2->priority - mi1->priority;
+
+ return strcmp (mi1->devnode, mi2->devnode);
+}
+
+/* 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);
+}
+
+static GList *
+gst_oss4_property_probe_get_mixer_devices (GstObject * obj, int fd,
+ struct oss_sysinfo *si)
+{
+ GList *m, *mixers = NULL;
+ GList *devices = NULL;
+
+ int i;
+
+ GST_LOG_OBJECT (obj, "%d mixer devices", si->nummixers);
+
+ /* first, find suitable mixer devices and sort by priority */
+ for (i = 0; i < si->nummixers; ++i) {
+ struct oss_mixerinfo mi = { 0, };
+
+ mi.dev = i;
+ if (ioctl (fd, SNDCTL_MIXERINFO, &mi) == -1) {
+ GST_DEBUG_OBJECT (obj, "MIXERINFO ioctl for device %d failed", i);
+ continue;
+ }
+
+ GST_INFO_OBJECT (obj, "mixer device %d:", i);
+ GST_INFO_OBJECT (obj, " enabled : %s", (mi.enabled) ? "yes" :
+ "no (powered off or unplugged)");
+ GST_INFO_OBJECT (obj, " priority : %d", mi.priority);
+ GST_INFO_OBJECT (obj, " devnode : %s", mi.devnode);
+ GST_INFO_OBJECT (obj, " handle : %s", mi.handle);
+ GST_INFO_OBJECT (obj, " caps : 0x%02x", mi.caps);
+ GST_INFO_OBJECT (obj, " name : %s", mi.name);
+
+ if (!mi.enabled) {
+ GST_DEBUG_OBJECT (obj, "mixer device is not usable/enabled, skipping");
+ continue;
+ }
+
+ /* only want mixers that control hardware directly */
+ if ((mi.caps & MIXER_CAP_VIRTUAL)) {
+ GST_DEBUG_OBJECT (obj, "mixer device is a virtual device, skipping");
+ continue;
+ }
+
+ mixers = g_list_insert_sorted (mixers, g_memdup (&mi, sizeof (mi)),
+ (GCompareFunc) oss4_mixerinfo_priority_cmp);
+ }
+
+ /* then create device list according to priority */
+ for (m = mixers; m != NULL; m = m->next) {
+ struct oss_mixerinfo *mi = (struct oss_mixerinfo *) m->data;
+
+ GST_LOG_OBJECT (obj, "mixer device: '%s'", mi->devnode);
+ devices = g_list_prepend (devices, g_strdup (mi->devnode));
+ g_free (m->data);
+ }
+ g_list_free (mixers);
+ mixers = NULL;
+
+ return g_list_reverse (devices);
+}
+
+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);
+}
+
+static GValueArray *
+gst_oss4_property_probe_get_values (GstPropertyProbe * probe,
+ guint prop_id, const GParamSpec * pspec)
+{
+ struct oss_sysinfo si = { {0,}, };
+ GstElementClass *klass;
+ GValueArray *array = NULL;
+ GstObject *obj;
+ GList *devices, *l;
+ int cap_mask, fd = -1;
+
+ if (!g_str_equal (pspec->name, "device")) {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
+ return NULL;
+ }
+
+ obj = GST_OBJECT (probe);
+
+ GST_OBJECT_LOCK (obj);
+
+ /* figure out whether the element is a source or sink */
+ klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (probe));
+ 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 if (GST_IS_OSS4_MIXER (probe)) {
+ fd = GST_OSS4_MIXER (probe)->fd;
+ cap_mask = 0;
+ } else {
+ GST_OBJECT_UNLOCK (obj);
+ g_return_val_if_reached (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;
+
+ if (cap_mask != 0) {
+ devices =
+ gst_oss4_property_probe_get_audio_devices (obj, fd, &si, cap_mask);
+ } else {
+ devices = gst_oss4_property_probe_get_mixer_devices (obj, fd, &si);
+ }
+
+ if (devices == NULL) {
+ 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;
+ }
+}
+
+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);
+}