summaryrefslogtreecommitdiff
path: root/sys/oss4/oss4-property-probe.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/oss4/oss4-property-probe.c')
-rwxr-xr-xsys/oss4/oss4-property-probe.c345
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