pulse: Add device monitors
authorOlivier Crête <olivier.crete@collabora.com>
Mon, 12 Aug 2013 15:49:21 +0000 (11:49 -0400)
committerOlivier Crête <tester@tester.ca>
Mon, 17 Mar 2014 00:37:54 +0000 (20:37 -0400)
https://bugzilla.gnome.org/show_bug.cgi?id=678402

ext/pulse/Makefile.am
ext/pulse/plugin.c
ext/pulse/pulsedevicemonitor.c [new file with mode: 0644]
ext/pulse/pulsedevicemonitor.h [new file with mode: 0644]

index a7dc7a3..e9e564f 100644 (file)
@@ -4,6 +4,7 @@ libgstpulse_la_SOURCES = \
        plugin.c \
        pulsesink.c \
        pulsesrc.c \
+       pulsedevicemonitor.c \
        pulseutil.c
 
 libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
@@ -16,5 +17,6 @@ libgstpulse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 noinst_HEADERS = \
        pulsesink.h \
        pulsesrc.h \
+       pulsedevicemonitor.h \
        pulseutil.h
 
index 505743b..ec9c89f 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "pulsesink.h"
 #include "pulsesrc.h"
+#include "pulsedevicemonitor.h"
 
 GST_DEBUG_CATEGORY (pulse_debug);
 
@@ -48,6 +49,10 @@ plugin_init (GstPlugin * plugin)
           GST_TYPE_PULSESRC))
     return FALSE;
 
+  if (!gst_device_monitor_register (plugin, "pulsemonitor",
+          GST_RANK_PRIMARY, GST_TYPE_PULSE_DEVICE_MONITOR))
+    return FALSE;
+
   GST_DEBUG_CATEGORY_INIT (pulse_debug, "pulse", 0, "PulseAudio elements");
   return TRUE;
 }
diff --git a/ext/pulse/pulsedevicemonitor.c b/ext/pulse/pulsedevicemonitor.c
new file mode 100644 (file)
index 0000000..83b8802
--- /dev/null
@@ -0,0 +1,672 @@
+/* GStreamer
+ * Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
+ *
+ * gstv4l2devicemonitor.c: V4l2 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 "pulsedevicemonitor.h"
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "pulsesrc.h"
+#include "pulsesink.h"
+#include "pulseutil.h"
+
+
+GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
+#define GST_CAT_DEFAULT pulse_debug
+
+
+static GstDevice *gst_pulse_device_new (guint id,
+    const gchar * device_name, GstCaps * caps, const gchar * internal_name,
+    GstPulseDeviceType type);
+
+G_DEFINE_TYPE (GstPulseDeviceMonitor, gst_pulse_device_monitor,
+    GST_TYPE_DEVICE_MONITOR);
+
+static void gst_pulse_device_monitor_finalize (GObject * object);
+static void gst_pulse_device_monitor_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_pulse_device_monitor_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+
+static GList *gst_pulse_device_monitor_probe (GstDeviceMonitor * monitor);
+static gboolean gst_pulse_device_monitor_start (GstDeviceMonitor * monitor);
+static void gst_pulse_device_monitor_stop (GstDeviceMonitor * monitor);
+
+enum
+{
+  PROP_0,
+  PROP_SERVER,
+  PROP_CLIENT_NAME,
+  PROP_LAST
+};
+
+
+static void
+gst_pulse_device_monitor_class_init (GstPulseDeviceMonitorClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstDeviceMonitorClass *dm_class = GST_DEVICE_MONITOR_CLASS (klass);
+  gchar *client_name;
+
+  gobject_class->set_property = gst_pulse_device_monitor_set_property;
+  gobject_class->get_property = gst_pulse_device_monitor_get_property;
+  gobject_class->finalize = gst_pulse_device_monitor_finalize;
+
+  dm_class->probe = gst_pulse_device_monitor_probe;
+  dm_class->start = gst_pulse_device_monitor_start;
+  dm_class->stop = gst_pulse_device_monitor_stop;
+
+  g_object_class_install_property (gobject_class,
+      PROP_SERVER,
+      g_param_spec_string ("server", "Server",
+          "The PulseAudio server to connect to", NULL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  client_name = gst_pulse_client_name ();
+  g_object_class_install_property (gobject_class,
+      PROP_CLIENT_NAME,
+      g_param_spec_string ("client-name", "Client Name",
+          "The PulseAudio client_name_to_use", client_name,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
+  g_free (client_name);
+
+  gst_device_monitor_class_set_static_metadata (dm_class,
+      "PulseAudio Device Monitor", "Sink/Source/Audio",
+      "List and monitor PulseAudio source and sink devices",
+      "Olivier Crete <olivier.crete@collabora.com>");
+}
+
+static void
+gst_pulse_device_monitor_init (GstPulseDeviceMonitor * self)
+{
+  self->client_name = gst_pulse_client_name ();
+}
+
+static void
+gst_pulse_device_monitor_finalize (GObject * object)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
+
+  g_free (self->client_name);
+  g_free (self->server);
+
+  G_OBJECT_CLASS (gst_pulse_device_monitor_parent_class)->finalize (object);
+}
+
+
+static void
+gst_pulse_device_monitor_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
+
+  switch (prop_id) {
+    case PROP_SERVER:
+      g_free (self->server);
+      self->server = g_value_dup_string (value);
+      break;
+    case PROP_CLIENT_NAME:
+      g_free (self->client_name);
+      if (!g_value_get_string (value)) {
+        GST_WARNING_OBJECT (self,
+            "Empty PulseAudio client name not allowed. "
+            "Resetting to default value");
+        self->client_name = gst_pulse_client_name ();
+      } else
+        self->client_name = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_pulse_device_monitor_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
+
+  switch (prop_id) {
+    case PROP_SERVER:
+      g_value_set_string (value, self->server);
+      break;
+    case PROP_CLIENT_NAME:
+      g_value_set_string (value, self->client_name);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+context_state_cb (pa_context * c, void *userdata)
+{
+  GstPulseDeviceMonitor *self = userdata;
+
+  switch (pa_context_get_state (c)) {
+    case PA_CONTEXT_READY:
+    case PA_CONTEXT_TERMINATED:
+    case PA_CONTEXT_FAILED:
+      pa_threaded_mainloop_signal (self->mainloop, 0);
+      break;
+
+    case PA_CONTEXT_UNCONNECTED:
+    case PA_CONTEXT_CONNECTING:
+    case PA_CONTEXT_AUTHORIZING:
+    case PA_CONTEXT_SETTING_NAME:
+      break;
+  }
+}
+
+static GstDevice *
+new_source (const pa_source_info * info)
+{
+  GstCaps *caps;
+  guint i;
+
+  caps = gst_caps_new_empty ();
+
+  for (i = 0; i < info->n_formats; i++)
+    gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
+
+  return gst_pulse_device_new (info->index, info->description,
+      caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE);
+}
+
+static GstDevice *
+new_sink (const pa_sink_info * info)
+{
+  GstCaps *caps;
+  guint i;
+
+  caps = gst_caps_new_empty ();
+
+  for (i = 0; i < info->n_formats; i++)
+    gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
+
+  return gst_pulse_device_new (info->index, info->description,
+      caps, info->name, GST_PULSE_DEVICE_TYPE_SINK);
+}
+
+static void
+get_source_info_cb (pa_context * context,
+    const pa_source_info * info, int eol, void *userdata)
+{
+  GstPulseDeviceMonitor *self = userdata;
+  GstDevice *dev;
+
+  if (eol) {
+    pa_threaded_mainloop_signal (self->mainloop, 0);
+    return;
+  }
+
+  dev = new_source (info);
+
+  if (dev)
+    gst_device_monitor_device_add (GST_DEVICE_MONITOR (self), dev);
+}
+
+static void
+get_sink_info_cb (pa_context * context,
+    const pa_sink_info * info, int eol, void *userdata)
+{
+  GstPulseDeviceMonitor *self = userdata;
+  GstDevice *dev;
+
+  if (eol) {
+    pa_threaded_mainloop_signal (self->mainloop, 0);
+    return;
+  }
+
+  dev = new_sink (info);
+
+  if (dev)
+    gst_device_monitor_device_add (GST_DEVICE_MONITOR (self), dev);
+}
+
+static void
+context_subscribe_cb (pa_context * context, pa_subscription_event_type_t type,
+    uint32_t idx, void *userdata)
+{
+  GstPulseDeviceMonitor *self = userdata;
+  GstDeviceMonitor *monitor = userdata;
+
+
+  if (!(type & (PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_SINK)))
+    return;
+
+
+  if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+    /* Microphone in the source output has changed */
+
+    if (type & PA_SUBSCRIPTION_EVENT_SOURCE)
+      pa_context_get_source_info_by_index (context, idx, get_source_info_cb,
+          self);
+    else if (type & PA_SUBSCRIPTION_EVENT_SINK)
+      pa_context_get_sink_info_by_index (context, idx, get_sink_info_cb, self);
+  } else if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
+      PA_SUBSCRIPTION_EVENT_REMOVE) {
+    GstPulseDevice *dev = NULL;
+    GList *item;
+
+    GST_OBJECT_LOCK (self);
+    for (item = monitor->devices; item; item = item->next) {
+      dev = item->data;
+
+      if (dev->device_index == idx) {
+        gst_object_ref (dev);
+        break;
+      }
+      dev = NULL;
+    }
+    GST_OBJECT_UNLOCK (self);
+
+    if (dev) {
+      gst_device_monitor_device_remove (GST_DEVICE_MONITOR (self),
+          GST_DEVICE (dev));
+      gst_object_unref (dev);
+    }
+  }
+}
+
+static void
+get_source_info_list_cb (pa_context * context, const pa_source_info * info,
+    int eol, void *userdata)
+{
+  GList **devices = userdata;
+
+  if (eol)
+    return;
+
+  *devices = g_list_prepend (*devices, gst_object_ref_sink (new_source (info)));
+}
+
+static void
+get_sink_info_list_cb (pa_context * context, const pa_sink_info * info,
+    int eol, void *userdata)
+{
+  GList **devices = userdata;
+
+  if (eol)
+    return;
+
+  *devices = g_list_prepend (*devices, gst_object_ref_sink (new_sink (info)));
+}
+
+static GList *
+gst_pulse_device_monitor_probe (GstDeviceMonitor * monitor)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
+  GList *devices = NULL;
+  pa_mainloop *m = NULL;
+  pa_context *c = NULL;
+  pa_operation *o;
+
+  if (!(m = pa_mainloop_new ()))
+    return NULL;
+
+  if (!(c = pa_context_new (pa_mainloop_get_api (m), self->client_name))) {
+    GST_ERROR_OBJECT (self, "Failed to create context");
+    goto failed;
+  }
+
+  if (pa_context_connect (c, self->server, 0, NULL) < 0) {
+    GST_ERROR_OBJECT (self, "Failed to connect: %s",
+        pa_strerror (pa_context_errno (self->context)));
+    goto failed;
+  }
+
+  for (;;) {
+    pa_context_state_t state;
+
+    state = pa_context_get_state (c);
+
+    if (!PA_CONTEXT_IS_GOOD (state)) {
+      GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Failed to connect: %s",
+              pa_strerror (pa_context_errno (c))), (NULL));
+      goto failed;
+    }
+
+    if (state == PA_CONTEXT_READY)
+      break;
+
+    /* Wait until the context is ready */
+    if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
+      goto failed;
+
+  }
+  GST_DEBUG_OBJECT (self, "connected");
+
+  o = pa_context_get_sink_info_list (c, get_sink_info_list_cb, &devices);
+  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
+      pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
+    if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
+      break;
+  }
+  pa_operation_unref (o);
+
+  o = pa_context_get_source_info_list (c, get_source_info_list_cb, &devices);
+  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
+      pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
+    if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
+      break;
+  }
+  pa_operation_unref (o);
+
+  pa_context_disconnect (c);
+  pa_mainloop_free (m);
+
+  return devices;
+
+failed:
+
+  return NULL;
+}
+
+static gboolean
+gst_pulse_device_monitor_start (GstDeviceMonitor * monitor)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
+  pa_operation *initial_operation;
+
+  if (!(self->mainloop = pa_threaded_mainloop_new ())) {
+    GST_ERROR_OBJECT (self, "Could not create pulseaudio mainloop");
+    goto mainloop_failed;
+  }
+  if (pa_threaded_mainloop_start (self->mainloop) < 0) {
+    GST_ERROR_OBJECT (self, "Could not start pulseaudio mainloop");
+    pa_threaded_mainloop_free (self->mainloop);
+    self->mainloop = NULL;
+    goto mainloop_failed;
+  }
+
+  pa_threaded_mainloop_lock (self->mainloop);
+
+  if (!(self->context =
+          pa_context_new (pa_threaded_mainloop_get_api (self->mainloop),
+              self->client_name))) {
+    GST_ERROR_OBJECT (self, "Failed to create context");
+    goto unlock_and_fail;
+  }
+
+  pa_context_set_state_callback (self->context, context_state_cb, self);
+  pa_context_set_subscribe_callback (self->context, context_subscribe_cb, self);
+
+
+  GST_DEBUG_OBJECT (self, "connect to server %s", GST_STR_NULL (self->server));
+
+  if (pa_context_connect (self->context, self->server, 0, NULL) < 0) {
+    GST_ERROR_OBJECT (self, "Failed to connect: %s",
+        pa_strerror (pa_context_errno (self->context)));
+    goto unlock_and_fail;
+  }
+
+  for (;;) {
+    pa_context_state_t state;
+
+    state = pa_context_get_state (self->context);
+
+    if (!PA_CONTEXT_IS_GOOD (state)) {
+      GST_ERROR_OBJECT (self, "Failed to connect: %s",
+          pa_strerror (pa_context_errno (self->context)));
+      goto unlock_and_fail;
+    }
+
+    if (state == PA_CONTEXT_READY)
+      break;
+
+    /* Wait until the context is ready */
+    pa_threaded_mainloop_wait (self->mainloop);
+  }
+  GST_DEBUG_OBJECT (self, "connected");
+
+  pa_context_subscribe (self->context,
+      PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
+
+  initial_operation = pa_context_get_source_info_list (self->context,
+      get_source_info_cb, self);
+  while (pa_operation_get_state (initial_operation) == PA_OPERATION_RUNNING) {
+    if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context))))
+      goto cancel_and_fail;
+
+    pa_threaded_mainloop_wait (self->mainloop);
+  }
+  pa_operation_unref (initial_operation);
+
+  initial_operation = pa_context_get_sink_info_list (self->context,
+      get_sink_info_cb, self);
+  if (!initial_operation)
+    goto unlock_and_fail;
+  while (pa_operation_get_state (initial_operation) == PA_OPERATION_RUNNING) {
+    if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context))))
+      goto cancel_and_fail;
+
+    pa_threaded_mainloop_wait (self->mainloop);
+  }
+  pa_operation_unref (initial_operation);
+
+  pa_threaded_mainloop_unlock (self->mainloop);
+
+  return TRUE;
+
+unlock_and_fail:
+  pa_threaded_mainloop_unlock (self->mainloop);
+  gst_pulse_device_monitor_stop (monitor);
+  return FALSE;
+
+mainloop_failed:
+  return FALSE;
+
+cancel_and_fail:
+  pa_operation_cancel (initial_operation);
+  pa_operation_unref (initial_operation);
+  goto unlock_and_fail;
+}
+
+static void
+gst_pulse_device_monitor_stop (GstDeviceMonitor * monitor)
+{
+  GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
+
+  pa_threaded_mainloop_stop (self->mainloop);
+
+  if (self->context) {
+    pa_context_disconnect (self->context);
+
+    /* Make sure we don't get any further callbacks */
+    pa_context_set_state_callback (self->context, NULL, NULL);
+    pa_context_set_subscribe_callback (self->context, NULL, NULL);
+
+    pa_context_unref (self->context);
+    self->context = NULL;
+  }
+
+  pa_threaded_mainloop_free (self->mainloop);
+  self->mainloop = NULL;
+}
+
+enum
+{
+  PROP_INTERNAL_NAME = 1,
+};
+
+G_DEFINE_TYPE (GstPulseDevice, gst_pulse_device, GST_TYPE_DEVICE);
+
+static void gst_pulse_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_pulse_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_pulse_device_finalize (GObject * object);
+static GstElement *gst_pulse_device_create_element (GstDevice * device,
+    const gchar * name);
+static gboolean gst_pulse_device_reconfigure_element (GstDevice * device,
+    GstElement * element);
+
+static void
+gst_pulse_device_class_init (GstPulseDeviceClass * klass)
+{
+  GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  dev_class->create_element = gst_pulse_device_create_element;
+  dev_class->reconfigure_element = gst_pulse_device_reconfigure_element;
+
+  object_class->get_property = gst_pulse_device_get_property;
+  object_class->set_property = gst_pulse_device_set_property;
+  object_class->finalize = gst_pulse_device_finalize;
+
+  g_object_class_install_property (object_class, PROP_INTERNAL_NAME,
+      g_param_spec_string ("internal-name", "Internal PulseAudio device name",
+          "The internal name of the PulseAudio device", "",
+          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gst_pulse_device_init (GstPulseDevice * device)
+{
+}
+
+static void
+gst_pulse_device_finalize (GObject * object)
+{
+  GstPulseDevice *device = GST_PULSE_DEVICE (object);
+
+  g_free (device->internal_name);
+
+  G_OBJECT_CLASS (gst_pulse_device_parent_class)->finalize (object);
+}
+
+static GstElement *
+gst_pulse_device_create_element (GstDevice * device, const gchar * name)
+{
+  GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
+  GstElement *elem;
+
+  elem = gst_element_factory_make (pulse_dev->element, name);
+  g_object_set (elem, "device", pulse_dev->internal_name, NULL);
+
+  return elem;
+}
+
+static gboolean
+gst_pulse_device_reconfigure_element (GstDevice * device, GstElement * element)
+{
+  GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
+
+  if (!strcmp (pulse_dev->element, "pulsesrc")) {
+    if (!GST_IS_PULSESRC (element))
+      return FALSE;
+  } else if (!strcmp (pulse_dev->element, "pulsesink")) {
+    if (!GST_IS_PULSESINK (element))
+      return FALSE;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  g_object_set (element, "device", pulse_dev->internal_name, NULL);
+
+  return TRUE;
+}
+
+static GstDevice *
+gst_pulse_device_new (guint device_index, const gchar * device_name,
+    GstCaps * caps, const gchar * internal_name, GstPulseDeviceType type)
+{
+  GstPulseDevice *gstdev;
+  const gchar *element;
+  const gchar *klass;
+
+  g_return_val_if_fail (device_name, NULL);
+  g_return_val_if_fail (internal_name, NULL);
+  g_return_val_if_fail (caps, NULL);
+
+
+  switch (type) {
+    case GST_PULSE_DEVICE_TYPE_SOURCE:
+      element = "pulsesrc";
+      klass = "Audio/Source";
+      break;
+    case GST_PULSE_DEVICE_TYPE_SINK:
+      element = "pulsesink";
+      klass = "Audio/Sink";
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+
+  gstdev = g_object_new (GST_TYPE_PULSE_DEVICE,
+      "display-name", device_name, "caps", caps, "klass", klass,
+      "internal-name", internal_name, NULL);
+
+  gstdev->device_index = device_index;
+  gstdev->element = element;
+
+  return GST_DEVICE (gstdev);
+}
+
+
+static void
+gst_pulse_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstPulseDevice *device;
+
+  device = GST_PULSE_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_pulse_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstPulseDevice *device;
+
+  device = GST_PULSE_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;
+  }
+}
diff --git a/ext/pulse/pulsedevicemonitor.h b/ext/pulse/pulsedevicemonitor.h
new file mode 100644 (file)
index 0000000..9d9070e
--- /dev/null
@@ -0,0 +1,97 @@
+/* GStreamer
+ * Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
+ *
+ * pulsedevicemonitor.h: 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_PULSE_DEVICE_MONITOR_H__
+#define __GST_PULSE_DEVICE_MONITOR_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pulse/pulseaudio.h>
+#include <pulse/thread-mainloop.h>
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstPulseDeviceMonitor GstPulseDeviceMonitor;
+typedef struct _GstPulseDeviceMonitorPrivate GstPulseDeviceMonitorPrivate;
+typedef struct _GstPulseDeviceMonitorClass GstPulseDeviceMonitorClass;
+
+#define GST_TYPE_PULSE_DEVICE_MONITOR                 (gst_pulse_device_monitor_get_type())
+#define GST_IS_PULSE_DEVICE_MONITOR(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PULSE_DEVICE_MONITOR))
+#define GST_IS_PULSE_DEVICE_MONITOR_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PULSE_DEVICE_MONITOR))
+#define GST_PULSE_DEVICE_MONITOR_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PULSE_DEVICE_MONITOR, GstPulseDeviceMonitorClass))
+#define GST_PULSE_DEVICE_MONITOR(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PULSE_DEVICE_MONITOR, GstPulseDeviceMonitor))
+#define GST_PULSE_DEVICE_MONITOR_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_MONITOR, GstPulseDeviceMonitorClass))
+#define GST_PULSE_DEVICE_MONITOR_CAST(obj)            ((GstPulseDeviceMonitor *)(obj))
+
+struct _GstPulseDeviceMonitor {
+  GstDeviceMonitor         parent;
+
+  gchar *server;
+  gchar *client_name;
+
+  pa_threaded_mainloop *mainloop;
+  pa_context *context;
+};
+
+typedef enum {
+  GST_PULSE_DEVICE_TYPE_SOURCE,
+  GST_PULSE_DEVICE_TYPE_SINK
+} GstPulseDeviceType;
+
+struct _GstPulseDeviceMonitorClass {
+  GstDeviceMonitorClass    parent_class;
+};
+
+GType        gst_pulse_device_monitor_get_type (void);
+
+
+typedef struct _GstPulseDevice GstPulseDevice;
+typedef struct _GstPulseDevicePrivate GstPulseDevicePrivate;
+typedef struct _GstPulseDeviceClass GstPulseDeviceClass;
+
+#define GST_TYPE_PULSE_DEVICE                 (gst_pulse_device_get_type())
+#define GST_IS_PULSE_DEVICE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PULSE_DEVICE))
+#define GST_IS_PULSE_DEVICE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PULSE_DEVICE))
+#define GST_PULSE_DEVICE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PULSE_DEVICE, GstPulseDeviceClass))
+#define GST_PULSE_DEVICE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PULSE_DEVICE, GstPulseDevice))
+#define GST_PULSE_DEVICE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstPulseDeviceClass))
+#define GST_PULSE_DEVICE_CAST(obj)            ((GstPulseDevice *)(obj))
+
+struct _GstPulseDevice {
+  GstDevice         parent;
+
+  guint             device_index;
+  gchar            *internal_name;
+  const gchar      *element;
+};
+
+struct _GstPulseDeviceClass {
+  GstDeviceClass    parent_class;
+};
+
+GType        gst_pulse_device_get_type (void);
+
+#endif /* __GST_PULSE_DEVICE_MONITOR_H__ */