> On Fri, Nov 22, 2024 at 10:34PM +0100, a...@caoua.org wrote:
>> On Sat, Nov 16, 2024 at 09:59:32PM +0100, na...@poczta.fm
wrote:
>> Hi misc@,
>> 
>> I noticed that GStreamer device monitor is not detecting any
Audio/Source and Audio/Sink devices on different laptops running OpenBSD
7.6-stable and sndiod. The following command reports only the embedded web
camera (Video/Source), whereas the expected output should also include at
least one Audio/Source and one Audio/Sink:
>> 
>>     $ gst-device-monitor-1.0
>> 
>> This seems to be related to the following issue reported earlier in
the context of Dino that relies on GStreamer as the selected backend:
>> 
>>     https://marc.info/?t=172654982900001&r=1&w=2
>> 
>> At the same time, I am able to record audio using the embedded
microphone and gst-launch-1.0 in the following way:
>> 
>>     # sysctl kern.audio.record=1
>>     $ gst-launch-1.0 -v sndiosrc ! queue ! audioconvert ! vorbisenc
! oggmux ! filesink location=sndiosrc.ogg
>> 
>> I wonder whether anyone else is also able to reproduce that issue,
or perhaps already has identified an effective solution or workaround in
that case? Any suggestions regarding further troubleshooting would of
course be much appreciated.
>> 
> 
> I'm able to reproduce the problem.
> 
> It looks like the device enumeration is probably not implemented. If
> so, there are just few lines missing from the sndio backend, they are
> supposed to return the device "list" (a single SIO_DEVANY entry).
> 
> After a quick look at the sources, it's not obvious how register a
> "device provider" to expose the device.
> 
> 

Hi Alexandre,

Thank you for your prompt reply.

Following your suggestion, I analyzed the existing implementation of the ALSA 
plugin, and as a quick PoC, I have adapted its code to work in the sndio case 
--- it succesfully exposes just the default Source and Sink audio devices to 
applications relying on GStreamer. Please find the related code at the end of 
this message (hopefully it will not be mangled by the email client). What do 
you think?

What I still need to figure out is how to enumerate all available Source/Sink 
sndio devices in the gst_sndio_device_provider_probe() function. Would you have 
any hints in that case?

With best regards,
Andrzej

================================================================================
--- gstsndio.c Sun Sep  1 06:43:36 2024
+++ gstsndio.c  Sun Nov 24 22:07:59 2024
@@ -21,6 +21,7 @@
 
 #include <stdio.h>
 #include "gstsndio.h"
+#include "gstsndiodeviceprovider.h"
 
 GST_DEBUG_CATEGORY (gst_sndio_debug);
 #define GST_CAT_DEFAULT gst_sndio_debug
@@ -33,6 +34,10 @@
 {
   GST_DEBUG_CATEGORY_INIT (gst_sndio_debug, "sndio", 0, "sndio plugins");
 
+  /* Register the corresponding device provider */
+  if (!gst_device_provider_register (plugin, "sndiodeviceprovider",
+          GST_RANK_PRIMARY + 20, gst_sndio_device_provider_get_type()))
+    return FALSE;
   /* prefer sndiosrc over pulsesrc (GST_RANK_PRIMARY + 10) */
   if (!gst_element_register (plugin, "sndiosrc", GST_RANK_PRIMARY + 20,
           gst_sndiosrc_get_type()))
================================================================================
gstsndiodeviceprovider.h:

/* Based on the implementation of gstalsadeviceprovider.h available here:
 * 
https://github.com/GStreamer/gst-plugins-base/blob/master/ext/alsa/gstalsadeviceprovider.h
 * and documentation here:
 * 
https://gstreamer.freedesktop.org/documentation/gstreamer/gstdeviceprovider.html?gi-language=c
 * Original license included below.
 *
 * GStreamer
 * Copyright (C) 2018 Thibault Saunier <tsaun...@igalia.com>
 *
 * alsadeviceprovider.c: alsa 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_SNDIODEVICEPROVIDER_H__
#define __GST_SNDIODEVICEPROVIDER_H__

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstsndio.h"
#include <gst/gst.h>

G_BEGIN_DECLS

typedef struct _GstSndioDeviceProvider GstSndioDeviceProvider;
typedef struct _GstSndioDeviceProviderClass GstSndioDeviceProviderClass;

#define GST_TYPE_SNDIO_DEVICE_PROVIDER \
  (gst_sndio_device_provider_get_type())
#define GST_IS_SNDIO_DEVICE_PROVIDER(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SNDIO_DEVICE_PROVIDER))
#define GST_IS_SNDIO_DEVICE_PROVIDER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SNDIO_DEVICE_PROVIDER))
#define GST_SNDIO_DEVICE_PROVIDER_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SNDIO_DEVICE_PROVIDER, \
  GstSndioDeviceProviderClass))
#define GST_SNDIO_DEVICE_PROVIDER(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SNDIO_DEVICE_PROVIDER, \
  GstSndioDeviceProvider))
#define GST_SNDIO_DEVICE_PROVIDER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, \
  GstSndioDeviceProviderClass))
#define GST_SNDIO_DEVICE_PROVIDER_CAST(obj) \
  ((GstSndioDeviceProvider *)(obj))

struct _GstSndioDeviceProvider {
  GstDeviceProvider parent;
};

struct _GstSndioDeviceProviderClass {
  GstDeviceProviderClass parent_class;
};

GType gst_sndio_device_provider_get_type(void);

typedef struct _GstSndioDevice GstSndioDevice;
typedef struct _GstSndioDeviceClass GstSndioDeviceClass;

#define GST_TYPE_SNDIO_DEVICE \
  (gst_sndio_device_get_type())
#define GST_IS_SNDIO_DEVICE(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SNDIO_DEVICE))
#define GST_IS_SNDIO_DEVICE_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SNDIO_DEVICE))
#define GST_SNDIO_DEVICE_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SNDIO_DEVICE, \
  GstSndioDeviceClass))
#define GST_SNDIO_DEVICE(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SNDIO_DEVICE, GstSndioDevice))
#define GST_SNDIO_DEVICE_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstSndioDeviceClass))
#define GST_SNDIO_DEVICE_CAST(obj) \
  ((GstSndioDevice *)(obj))

struct _GstSndioDevice {
  GstDevice    parent;
  gint         mode;
  gchar        internal_name;
  const gchar  element;
};

struct _GstSndioDeviceClass {
  GstDeviceClass parent_class;
};

GType gst_sndio_device_get_type(void);

G_END_DECLS

#endif /* __GST_SNDIODEVICEPROVIDER_H__ */

================================================================================
gstsndiodeviceprovider.c:

/* Based on the implementation of gstalsadeviceprovider.c available here:
 * 
https://github.com/GStreamer/gst-plugins-base/blob/master/ext/alsa/gstalsadeviceprovider.c
 * and documentation here:
 * 
https://gstreamer.freedesktop.org/documentation/gstreamer/gstdeviceprovider.html?gi-language=c
 * Original license included below.
 *
 * GStreamer
 * Copyright (C) 2018 Thibault Saunier <tsaun...@igalia.com>
 *
 * alsadeviceprovider.c: alsa 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 "gstsndiodeviceprovider.h"
#include "gstsndio.h"
#include <gst/gst.h>
#include <sndio.h>

static GstDevice *
gst_sndio_device_new( const gchar *device_name,
                      GstCaps *caps,
                      const gchar *internal_name,
                      gint stream_mode,
                      GstStructure *properties );

G_DEFINE_TYPE( GstSndioDeviceProvider, gst_sndio_device_provider,
               GST_TYPE_DEVICE_PROVIDER );

static GList *
gst_sndio_device_provider_probe( GstDeviceProvider *provider )
{
  GList *list = NULL;
  gint i;
  gint stream_modes[] = { SIO_REC, SIO_PLAY };

  GST_INFO_OBJECT( provider, "Probing sndio devices.." );

  for ( i = 0; i < G_N_ELEMENTS( stream_modes ); i++ )
  {
    struct gstsndio sio;
    GstDevice *device = NULL;
    GstStructure *properties = NULL;

    sio.device = g_strdup( SIO_DEVANY );
    sio.mode = stream_modes[ i ];

    if ( !gst_sndio_open( &sio, sio.mode ) )
    {
      GST_INFO_OBJECT( provider, "Couldn't open sndio device" );
      continue;
    }

    GST_DEBUG( "caps are %s", gst_caps_to_string( sio.cur_caps ) );
    properties = gst_caps_get_structure( sio.cur_caps, 0 );
    device = gst_sndio_device_new( sio.device, sio.cur_caps, sio.device,
                                   stream_modes[ i ], properties );

    if ( device )
    {
      list = g_list_prepend( list, device );
    }

    gst_sndio_close( &sio );
  }

  return list;
}

static void 
gst_sndio_device_provider_class_init( GstSndioDeviceProviderClass *klass )
{
  GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS( klass );

  dm_class->probe = gst_sndio_device_provider_probe;

  gst_device_provider_class_set_static_metadata( dm_class,
      "Sndio Device Provider", "Sink/Source/Audio",
      "Lists and provides Sndio source and sink devices",
      " " );
}

static void 
gst_sndio_device_provider_init( GstSndioDeviceProvider *self )
{
}

/*** GstSndioDevice implementation ******/
enum
{
  PROP_INTERNAL_NAME = 1,
};

G_DEFINE_TYPE( GstSndioDevice, gst_sndio_device, GST_TYPE_DEVICE );

static GstElement *
gst_sndio_device_create_element( GstDevice *device, const gchar *name )
{
  GstSndioDevice *sndio_dev = GST_SNDIO_DEVICE( device );
  GstElement *elem;

  elem = gst_element_factory_make( sndio_dev->element, name );
  g_object_set( elem, "device", sndio_dev->internal_name, NULL );

  return elem;
}

static gboolean 
gst_sndio_device_reconfigure_element( GstDevice *device, GstElement *element )
{
  GstSndioDevice *sndio_dev = GST_SNDIO_DEVICE( device );

  g_object_set( element, "device", sndio_dev->internal_name, NULL );

  return TRUE;
}


static void 
gst_sndio_device_get_property( GObject *object, guint prop_id, GValue *value, 
    GParamSpec *pspec )
{
  GstSndioDevice *device;

  device = GST_SNDIO_DEVICE_CAST( object );

  switch ( prop_id )
  {
    case PROP_INTERNAL_NAME:
      g_value_set_string( value, device->internal_name );
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
      break;
  }
}

static void 
gst_sndio_device_set_property( GObject *object, guint prop_id, 
    const GValue *value, GParamSpec *pspec )
{
  GstSndioDevice *device;

  device = GST_SNDIO_DEVICE_CAST( object );

  switch ( prop_id )
  {
    case PROP_INTERNAL_NAME:
      device->internal_name = g_value_dup_string( value );
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
      break;
  }
}

static void 
gst_sndio_device_finalize( GObject *object )
{
  GstSndioDevice *device = GST_SNDIO_DEVICE( object );

  g_free( device->internal_name );

  G_OBJECT_CLASS( gst_sndio_device_parent_class )->finalize( object );
}

static void 
gst_sndio_device_class_init( GstSndioDeviceClass *klass )
{
  GstDeviceClass *dev_class = GST_DEVICE_CLASS( klass );
  GObjectClass *object_class = G_OBJECT_CLASS( klass );

  dev_class->create_element = gst_sndio_device_create_element;
  dev_class->reconfigure_element = gst_sndio_device_reconfigure_element;

  object_class->get_property = gst_sndio_device_get_property;
  object_class->set_property = gst_sndio_device_set_property;
  object_class->finalize = gst_sndio_device_finalize;

  g_object_class_install_property( object_class,
                                   PROP_INTERNAL_NAME,
                                   g_param_spec_string( "internal-name",
                                     "Internal SndioAudio device name",
                                     "The internal name of the SndioAudio 
device",
                                     "",
                                     G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE 
| G_PARAM_CONSTRUCT_ONLY )
                                 );
}

static void 
gst_sndio_device_init( GstSndioDevice *device )
{
}

/* Takes ownership of @caps and @props */
static GstDevice *
gst_sndio_device_new( const gchar *device_name,
                      GstCaps *caps,
                      const gchar *internal_name,
                      gint stream_mode,
                      GstStructure *props )
{
  GstSndioDevice *gstdev;
  const gchar *element = NULL;
  const gchar *klass = NULL;

  g_return_val_if_fail( device_name, NULL );
  g_return_val_if_fail( internal_name, NULL );
  g_return_val_if_fail( caps, NULL );

  switch ( stream_mode )
  {
    case SIO_REC:
      element = "sndiosrc";
      klass = "Audio/Source";
      break;

    case SIO_PLAY:
      element = "sndiosink";
      klass = "Audio/Sink";
      break;

    default:
      g_assert_not_reached();
      break;
  }

  gstdev = g_object_new( GST_TYPE_SNDIO_DEVICE,
                         "display-name", device_name,
                         "caps", caps,
                         "device-class", klass,
                         "internal-name", internal_name,
                         "properties", props,
                         NULL );

  gstdev->mode = stream_mode;
  gstdev->element = element;

  return GST_DEVICE( gstdev );
}
================================================================================

Reply via email to