From d228b8d96f3608104018aa81321eb657637b3ed9 Mon Sep 17 00:00:00 2001 From: Matthieu Volat Date: Thu, 25 May 2023 20:39:03 +0200 Subject: [PATCH] oss: add a GstDeviceProvider plugin Based on Alsa's GstDeviceProvider structure, relies on sndstat file for OSS device enumeration but uses already existing utils to query caps and names. Reviewed and thanks to @slomo Part-of: --- .../gst-plugins-good/sys/oss/gstossaudio.c | 3 + .../sys/oss/gstossaudioelement.c | 4 + .../sys/oss/gstossaudioelements.h | 2 + .../sys/oss/gstossdeviceprovider.c | 303 ++++++++++++++++++ .../sys/oss/gstossdeviceprovider.h | 88 +++++ .../gst-plugins-good/sys/oss/meson.build | 2 +- 6 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.c create mode 100644 subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.h diff --git a/subprojects/gst-plugins-good/sys/oss/gstossaudio.c b/subprojects/gst-plugins-good/sys/oss/gstossaudio.c index 9a9383fe51..c6d2e16315 100644 --- a/subprojects/gst-plugins-good/sys/oss/gstossaudio.c +++ b/subprojects/gst-plugins-good/sys/oss/gstossaudio.c @@ -25,6 +25,7 @@ #include "common.h" #include "gstossaudioelements.h" +#include "gstossdeviceprovider.h" #include "gstosssink.h" #include "gstosssrc.h" @@ -32,6 +33,8 @@ static gboolean plugin_init (GstPlugin * plugin) { + GST_DEVICE_PROVIDER_REGISTER (ossdeviceprovider, plugin); + GST_ELEMENT_REGISTER (osssrc, plugin); GST_ELEMENT_REGISTER (osssink, plugin); diff --git a/subprojects/gst-plugins-good/sys/oss/gstossaudioelement.c b/subprojects/gst-plugins-good/sys/oss/gstossaudioelement.c index 50e06a9ed0..4cf5a63bbe 100644 --- a/subprojects/gst-plugins-good/sys/oss/gstossaudioelement.c +++ b/subprojects/gst-plugins-good/sys/oss/gstossaudioelement.c @@ -25,10 +25,14 @@ #include "common.h" #include "gstossaudioelements.h" +#include "gstossdeviceprovider.h" GST_DEBUG_CATEGORY (oss_debug); #define GST_CAT_DEFAULT oss_debug +GST_DEVICE_PROVIDER_REGISTER_DEFINE (ossdeviceprovider, "ossdeviceprovider", + GST_RANK_SECONDARY, GST_TYPE_OSS_DEVICE_PROVIDER); + void oss_element_init (GstPlugin * plugin) { diff --git a/subprojects/gst-plugins-good/sys/oss/gstossaudioelements.h b/subprojects/gst-plugins-good/sys/oss/gstossaudioelements.h index 4770a76c6e..67430b612b 100644 --- a/subprojects/gst-plugins-good/sys/oss/gstossaudioelements.h +++ b/subprojects/gst-plugins-good/sys/oss/gstossaudioelements.h @@ -34,6 +34,8 @@ G_GNUC_INTERNAL void oss_element_init (GstPlugin * plugin); GST_ELEMENT_REGISTER_DECLARE (osssink); GST_ELEMENT_REGISTER_DECLARE (osssrc); +GST_DEVICE_PROVIDER_REGISTER_DECLARE(ossdeviceprovider); + G_END_DECLS #endif /* __GST_OSS_ELEMENTS_H__ */ diff --git a/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.c b/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.c new file mode 100644 index 0000000000..b8984d0e82 --- /dev/null +++ b/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.c @@ -0,0 +1,303 @@ +/* GStreamer + * Copyright (C) 2023 Matthieu Volat + * + * ossdeviceprovider.c: OSS device probing and monitoring + * + * 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 "gstossdeviceprovider.h" +#include "gstosshelper.h" +#include +#include +#include +#include + + +static GstDevice *gst_oss_device_new (const gchar * device_name, GstCaps * caps, + const gchar * device, GstOssDeviceType type); + +G_DEFINE_TYPE (GstOssDeviceProvider, gst_oss_device_provider, + GST_TYPE_DEVICE_PROVIDER); + + +static GstDevice * +add_device (GstDeviceProvider * provider, GstOssDeviceType type, gint devno) +{ + gchar devpath[64]; + gchar mixpath[64]; + gint fd; + GstCaps *caps; + gchar *name; + GstDevice *device; + + snprintf (devpath, sizeof (devpath), "/dev/dsp%u", devno); + snprintf (mixpath, sizeof (mixpath), "/dev/mixer%u", devno); + + switch (type) { + case GST_OSS_DEVICE_TYPE_SOURCE: + fd = open (devpath, O_RDONLY); + break; + case GST_OSS_DEVICE_TYPE_SINK: + fd = open (devpath, O_WRONLY); + break; + default: + g_assert_not_reached (); + break; + } + if (fd == -1) { + GST_WARNING_OBJECT (provider, "Could open device %s for introspection", + devpath); + return NULL; + } + caps = gst_oss_helper_probe_caps (fd); + close (fd); + name = gst_oss_helper_get_card_name (mixpath); + + device = gst_oss_device_new (name, caps, devpath, type); + g_free (name); + return device; +} + +static GList * +gst_oss_device_provider_probe (GstDeviceProvider * provider) +{ + FILE *sndstat_handle; + gchar *sndstat_line = NULL; + size_t sndstat_line_len = 0; + gint sndstat_device_section = 0; + gint ossdevno; + gboolean play, rec; + GstDevice *device; + GList *list = NULL; + + GST_INFO_OBJECT (provider, "Probing OSS devices"); + if (((sndstat_handle = fopen ("/dev/sndstat", "r")) == NULL) + && ((sndstat_handle = fopen ("/proc/sndstat", "r")) == NULL) + && ((sndstat_handle = fopen ("/proc/asound/sndstat", "r")) == NULL)) { + /* Cannot evaluate OSS devices without this file */ + GST_WARNING_OBJECT (provider, "No sndstat file found"); + goto beach; + } + + while (!feof (sndstat_handle)) { + if (getline (&sndstat_line, &sndstat_line_len, sndstat_handle) == -1) { + break; + } + g_strstrip (sndstat_line); + + if (!sndstat_device_section) { + sndstat_device_section = g_str_equal (sndstat_line, "Audio devices:") + || g_str_equal (sndstat_line, "Installed devices:") + || g_str_equal (sndstat_line, "Installed devices from userspace:"); + continue; + } + + if ((sscanf (sndstat_line, "pcm%u:", &ossdevno) == 1) + || (sscanf (sndstat_line, "%u:", &ossdevno) == 1)) { + /* At least on FreeBSD, these keywords can be different if hw.snd.verbose is not 0 */ + if (strstr (sndstat_line, "(play/rec)") != NULL) { + play = rec = TRUE; + } else if (strstr (sndstat_line, "(play)") != NULL) { + play = TRUE, rec = FALSE; + } else if (strstr (sndstat_line, "(rec)") != NULL) { + play = FALSE, rec = TRUE; + } else { + play = rec = FALSE; /* Or should we assume play/rec? */ + } + + if (play) { + device = add_device (provider, GST_OSS_DEVICE_TYPE_SINK, ossdevno); + if (device != NULL) { + list = g_list_append (list, device); + } + } + if (rec) { + device = add_device (provider, GST_OSS_DEVICE_TYPE_SOURCE, ossdevno); + if (device != NULL) { + list = g_list_append (list, device); + } + } + } + } + + free (sndstat_line); + fclose (sndstat_handle); + +beach: + return list; +} + + +static void +gst_oss_device_provider_class_init (GstOssDeviceProviderClass * klass) +{ + GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass); + + dm_class->probe = gst_oss_device_provider_probe; + + gst_device_provider_class_set_static_metadata (dm_class, + "OSS Device Provider", "Sink/Source/Audio", + "List and provides OSS source and sink devices", + "Matthieu Volat "); +} + +static void +gst_oss_device_provider_init (GstOssDeviceProvider * self) +{ +} + +/*** GstOssDevice implementation ******/ +enum +{ + PROP_DEVICE_PATH = 1, +}; + + +G_DEFINE_TYPE (GstOssDevice, gst_oss_device, GST_TYPE_DEVICE); + +static GstElement * +gst_oss_device_create_element (GstDevice * device, const gchar * name) +{ + GstOssDevice *oss_dev = GST_OSS_DEVICE (device); + GstElement *elem; + + elem = gst_element_factory_make (oss_dev->element, name); + g_object_set (elem, "device", oss_dev->device_path, NULL); + + return elem; +} + +static gboolean +gst_oss_device_reconfigure_element (GstDevice * device, GstElement * element) +{ + GstOssDevice *oss_dev = GST_OSS_DEVICE (device); + + g_object_set (element, "device", oss_dev->device_path, NULL); + + return TRUE; +} + +static void +gst_oss_device_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstOssDevice *device; + + device = GST_OSS_DEVICE_CAST (object); + + switch (prop_id) { + case PROP_DEVICE_PATH: + g_value_set_string (value, device->device_path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_oss_device_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOssDevice *device; + + device = GST_OSS_DEVICE_CAST (object); + + switch (prop_id) { + case PROP_DEVICE_PATH: + device->device_path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_oss_device_finalize (GObject * object) +{ + GstOssDevice *device = GST_OSS_DEVICE (object); + + g_free (device->device_path); + + G_OBJECT_CLASS (gst_oss_device_parent_class)->finalize (object); +} + +static void +gst_oss_device_class_init (GstOssDeviceClass * klass) +{ + GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + dev_class->create_element = gst_oss_device_create_element; + dev_class->reconfigure_element = gst_oss_device_reconfigure_element; + + object_class->get_property = gst_oss_device_get_property; + object_class->set_property = gst_oss_device_set_property; + object_class->finalize = gst_oss_device_finalize; + + g_object_class_install_property (object_class, PROP_DEVICE_PATH, + g_param_spec_string ("device-path", "OSS device path", + "The path of the OSS device", "", + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gst_oss_device_init (GstOssDevice * device) +{ +} + +static GstDevice * +gst_oss_device_new (const gchar * device_name, + GstCaps * caps, const gchar * device_path, GstOssDeviceType type) +{ + GstOssDevice *gstdev; + const gchar *element = NULL; + const gchar *klass = NULL; + + g_return_val_if_fail (device_name, NULL); + g_return_val_if_fail (device_path, NULL); + g_return_val_if_fail (caps, NULL); + + switch (type) { + case GST_OSS_DEVICE_TYPE_SOURCE: + element = "osssrc"; + klass = "Audio/Source"; + break; + case GST_OSS_DEVICE_TYPE_SINK: + element = "osssink"; + klass = "Audio/Sink"; + break; + default: + g_assert_not_reached (); + break; + } + + gstdev = g_object_new (GST_TYPE_OSS_DEVICE, + "display-name", device_name, "caps", caps, "device-class", klass, + "device-path", device_path, NULL); + + gstdev->element = element; + + gst_caps_unref (caps); + + return GST_DEVICE (gstdev); +} diff --git a/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.h b/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.h new file mode 100644 index 0000000000..59ef0f26df --- /dev/null +++ b/subprojects/gst-plugins-good/sys/oss/gstossdeviceprovider.h @@ -0,0 +1,88 @@ +/* GStreamer + * Copyright (C) 2023 Matthieu Volat + * + * ossdeviceprovider.c: OSS device probing and monitoring + * + * 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. + */ + + +#ifndef __GST_OSS_DEVICE_PROVIDER_H__ +#define __GST_OSS_DEVICE_PROVIDER_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +G_BEGIN_DECLS + +typedef struct _GstOssDeviceProvider GstOssDeviceProvider; +typedef struct _GstOssDeviceProviderClass GstOssDeviceProviderClass; + +#define GST_TYPE_OSS_DEVICE_PROVIDER (gst_oss_device_provider_get_type()) +#define GST_IS_OSS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OSS_DEVICE_PROVIDER)) +#define GST_IS_OSS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OSS_DEVICE_PROVIDER)) +#define GST_OSS_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OSS_DEVICE_PROVIDER, GstOssDeviceProviderClass)) +#define GST_OSS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OSS_DEVICE_PROVIDER, GstOssDeviceProvider)) +#define GST_OSS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstOssDeviceProviderClass)) +#define GST_OSS_DEVICE_PROVIDER_CAST(obj) ((GstOssDeviceProvider *)(obj)) + +struct _GstOssDeviceProvider { + GstDeviceProvider parent; +}; + +struct _GstOssDeviceProviderClass { + GstDeviceProviderClass parent_class; +}; + +GType gst_oss_device_provider_get_type (void); + + +typedef struct _GstOssDevice GstOssDevice; +typedef struct _GstOssDeviceClass GstOssDeviceClass; + +#define GST_TYPE_OSS_DEVICE (gst_oss_device_get_type()) +#define GST_IS_OSS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OSS_DEVICE)) +#define GST_IS_OSS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OSS_DEVICE)) +#define GST_OSS_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OSS_DEVICE, GstOssDeviceClass)) +#define GST_OSS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OSS_DEVICE, GstOssDevice)) +#define GST_OSS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstOssDeviceClass)) +#define GST_OSS_DEVICE_CAST(obj) ((GstOssDevice *)(obj)) + +typedef enum +{ + GST_OSS_DEVICE_TYPE_INVALID = 0, + GST_OSS_DEVICE_TYPE_SOURCE, + GST_OSS_DEVICE_TYPE_SINK +} GstOssDeviceType; + +struct _GstOssDevice { + GstDevice parent; + + gchar *device_path; + const gchar *element; +}; + +struct _GstOssDeviceClass { + GstDeviceClass parent_class; +}; + +GType gst_oss_device_get_type (void); + +G_END_DECLS +#endif /* __GST_OSS_DEVICE_PROVIDER_H__ */ diff --git a/subprojects/gst-plugins-good/sys/oss/meson.build b/subprojects/gst-plugins-good/sys/oss/meson.build index 51cc4d34c6..b91b5c02b2 100644 --- a/subprojects/gst-plugins-good/sys/oss/meson.build +++ b/subprojects/gst-plugins-good/sys/oss/meson.build @@ -26,7 +26,7 @@ endif if have_oss plugins += [library('gstossaudio', - 'gstossaudio.c', 'gstossaudioelement.c', 'gstosshelper.c', 'gstosssink.c', 'gstosssrc.c', + 'gstossaudio.c', 'gstossaudioelement.c', 'gstossdeviceprovider.c', 'gstosshelper.c', 'gstosssink.c', 'gstosssrc.c', c_args : gst_plugins_good_args, include_directories : [configinc, libsinc], dependencies : [gstaudio_dep, gstbase_dep],