device: Enforce that elements created by gst_device_create_element() are floating
[platform/upstream/gstreamer.git] / gst / gstdevicemonitor.c
index 9d59211..088eb4c 100644 (file)
@@ -21,6 +21,7 @@
 
 /**
  * SECTION:gstdevicemonitor
+ * @title: GstDeviceMonitor
  * @short_description: A device monitor and prober
  * @see_also: #GstDevice, #GstDeviceProvider
  *
  * messages on its #GstBus for devices that have been added and
  * removed.
  *
+ * The device monitor will monitor all devices matching the filters that
+ * the application has set.
+ *
+ * The basic use pattern of a device monitor is as follows:
+ * |[
+ *   static gboolean
+ *   my_bus_func (GstBus * bus, GstMessage * message, gpointer user_data)
+ *   {
+ *      GstDevice *device;
+ *      gchar *name;
+ *
+ *      switch (GST_MESSAGE_TYPE (message)) {
+ *        case GST_MESSAGE_DEVICE_ADDED:
+ *          gst_message_parse_device_added (message, &device);
+ *          name = gst_device_get_display_name (device);
+ *          g_print("Device added: %s\n", name);
+ *          g_free (name);
+ *          gst_object_unref (device);
+ *          break;
+ *        case GST_MESSAGE_DEVICE_REMOVED:
+ *          gst_message_parse_device_removed (message, &device);
+ *          name = gst_device_get_display_name (device);
+ *          g_print("Device removed: %s\n", name);
+ *          g_free (name);
+ *          gst_object_unref (device);
+ *          break;
+ *        default:
+ *          break;
+ *      }
+ *
+ *      return G_SOURCE_CONTINUE;
+ *   }
+ *
+ *   GstDeviceMonitor *
+ *   setup_raw_video_source_device_monitor (void) {
+ *      GstDeviceMonitor *monitor;
+ *      GstBus *bus;
+ *      GstCaps *caps;
+ *
+ *      monitor = gst_device_monitor_new ();
+ *
+ *      bus = gst_device_monitor_get_bus (monitor);
+ *      gst_bus_add_watch (bus, my_bus_func, NULL);
+ *      gst_object_unref (bus);
+ *
+ *      caps = gst_caps_new_empty_simple ("video/x-raw");
+ *      gst_device_monitor_add_filter (monitor, "Video/Source", caps);
+ *      gst_caps_unref (caps);
+ *
+ *      gst_device_monitor_start (monitor);
+ *
+ *      return monitor;
+ *   }
+ * ]|
+ *
  * Since: 1.4
  */
 
@@ -51,23 +107,125 @@ struct _GstDeviceMonitorPrivate
   GPtrArray *providers;
   guint cookie;
 
-  GstCaps *caps;
-  gchar *classes;
+  GPtrArray *filters;
+
+  guint last_id;
+  GList *hidden;
+  gboolean show_all;
 };
 
+#define DEFAULT_SHOW_ALL        FALSE
+
+enum
+{
+  PROP_SHOW_ALL = 1,
+};
 
-G_DEFINE_TYPE (GstDeviceMonitor, gst_device_monitor, GST_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_PRIVATE (GstDeviceMonitor, gst_device_monitor,
+    GST_TYPE_OBJECT);
 
 static void gst_device_monitor_dispose (GObject * object);
 
+struct DeviceFilter
+{
+  guint id;
+
+  gchar **classesv;
+  GstCaps *caps;
+};
+
+static void
+device_filter_free (struct DeviceFilter *filter)
+{
+  g_strfreev (filter->classesv);
+  gst_caps_unref (filter->caps);
+
+  g_slice_free (struct DeviceFilter, filter);
+}
+
+static void
+gst_device_monitor_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstDeviceMonitor *monitor = GST_DEVICE_MONITOR (object);
+
+  switch (prop_id) {
+    case PROP_SHOW_ALL:
+      g_value_set_boolean (value,
+          gst_device_monitor_get_show_all_devices (monitor));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_device_monitor_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstDeviceMonitor *monitor = GST_DEVICE_MONITOR (object);
+
+  switch (prop_id) {
+    case PROP_SHOW_ALL:
+      gst_device_monitor_set_show_all_devices (monitor,
+          g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+
 static void
 gst_device_monitor_class_init (GstDeviceMonitorClass * klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  g_type_class_add_private (klass, sizeof (GstDeviceMonitorPrivate));
-
+  object_class->get_property = gst_device_monitor_get_property;
+  object_class->set_property = gst_device_monitor_set_property;
   object_class->dispose = gst_device_monitor_dispose;
+
+  g_object_class_install_property (object_class, PROP_SHOW_ALL,
+      g_param_spec_boolean ("show-all", "Show All",
+          "Show all devices, even those from hidden providers",
+          DEFAULT_SHOW_ALL, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+}
+
+/* must be called with monitor lock */
+static gboolean
+is_provider_hidden (GstDeviceMonitor * monitor, GList * hidden,
+    GstDeviceProvider * provider)
+{
+  GstDeviceProviderFactory *factory;
+
+  if (monitor->priv->show_all)
+    return FALSE;
+
+  factory = gst_device_provider_get_factory (provider);
+  if (g_list_find_custom (hidden, GST_OBJECT_NAME (factory),
+          (GCompareFunc) g_strcmp0))
+    return TRUE;
+
+  return FALSE;
+}
+
+/* must be called with monitor lock */
+static void
+update_hidden_providers_list (GList ** hidden, GstDeviceProvider * provider)
+{
+  gchar **obs;
+
+  obs = gst_device_provider_get_hidden_providers (provider);
+  if (obs) {
+    gint i;
+
+    for (i = 0; obs[i]; i++)
+      *hidden = g_list_prepend (*hidden, obs[i]);
+
+    g_free (obs);
+  }
 }
 
 static void
@@ -76,23 +234,45 @@ bus_sync_message (GstBus * bus, GstMessage * message,
 {
   GstMessageType type = GST_MESSAGE_TYPE (message);
 
-  if (type == GST_MESSAGE_DEVICE_ADDED || type == GST_MESSAGE_DEVICE_REMOVED) {
-    gboolean matches;
-    GstCaps *caps;
+  if (type == GST_MESSAGE_DEVICE_ADDED || type == GST_MESSAGE_DEVICE_REMOVED ||
+      type == GST_MESSAGE_DEVICE_CHANGED) {
+    gboolean matches = TRUE;
     GstDevice *device;
+    GstDeviceProvider *provider;
 
     if (type == GST_MESSAGE_DEVICE_ADDED)
       gst_message_parse_device_added (message, &device);
-    else
+    else if (type == GST_MESSAGE_DEVICE_REMOVED)
       gst_message_parse_device_removed (message, &device);
+    else
+      gst_message_parse_device_changed (message, &device, NULL);
 
     GST_OBJECT_LOCK (monitor);
-    caps = gst_device_get_caps (device);
-    matches = gst_caps_can_intersect (monitor->priv->caps, caps) &&
-        gst_device_has_classes (device, monitor->priv->classes);
-    gst_caps_unref (caps);
+    provider =
+        GST_DEVICE_PROVIDER (gst_object_get_parent (GST_OBJECT (device)));
+    if (is_provider_hidden (monitor, monitor->priv->hidden, provider)) {
+      matches = FALSE;
+    } else {
+      guint i;
+
+      for (i = 0; i < monitor->priv->filters->len; i++) {
+        struct DeviceFilter *filter =
+            g_ptr_array_index (monitor->priv->filters, i);
+        GstCaps *caps;
+
+        caps = gst_device_get_caps (device);
+        matches = gst_caps_can_intersect (filter->caps, caps) &&
+            gst_device_has_classesv (device, filter->classesv);
+        gst_caps_unref (caps);
+        if (matches)
+          break;
+      }
+    }
     GST_OBJECT_UNLOCK (monitor);
 
+    gst_object_unref (provider);
+    gst_object_unref (device);
+
     if (matches)
       gst_bus_post (monitor->priv->bus, gst_message_ref (message));
   }
@@ -102,40 +282,18 @@ bus_sync_message (GstBus * bus, GstMessage * message,
 static void
 gst_device_monitor_init (GstDeviceMonitor * self)
 {
-  GList *factories;
+  self->priv = gst_device_monitor_get_instance_private (self);
 
-  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
-      GST_TYPE_DEVICE_MONITOR, GstDeviceMonitorPrivate);
+  self->priv->show_all = DEFAULT_SHOW_ALL;
 
   self->priv->bus = gst_bus_new ();
   gst_bus_set_flushing (self->priv->bus, TRUE);
 
   self->priv->providers = g_ptr_array_new ();
-  self->priv->caps = gst_caps_new_any ();
-  self->priv->classes = g_strdup ("");
-
-  factories =
-      gst_device_provider_factory_list_get_device_providers (self->
-      priv->classes, 1);
-
-  while (factories) {
-    GstDeviceProviderFactory *factory = factories->data;
-    GstDeviceProvider *provider;
+  self->priv->filters = g_ptr_array_new_with_free_func (
+      (GDestroyNotify) device_filter_free);
 
-    factories = g_list_remove (factories, factory);
-
-    provider = gst_device_provider_factory_get (factory);
-    if (provider) {
-      GstBus *bus = gst_device_provider_get_bus (provider);
-
-      gst_bus_enable_sync_message_emission (bus);
-      g_signal_connect (bus, "sync-message",
-          G_CALLBACK (bus_sync_message), self);
-      g_ptr_array_add (self->priv->providers, provider);
-    }
-
-    gst_object_unref (factory);
-  }
+  self->priv->last_id = 1;
 }
 
 
@@ -145,7 +303,7 @@ gst_device_monitor_remove (GstDeviceMonitor * self, guint i)
   GstDeviceProvider *provider = g_ptr_array_index (self->priv->providers, i);
   GstBus *bus;
 
-  g_ptr_array_remove_index_fast (self->priv->providers, i);
+  g_ptr_array_remove_index (self->priv->providers, i);
 
   bus = gst_device_provider_get_bus (provider);
   g_signal_handlers_disconnect_by_func (bus, bus_sync_message, self);
@@ -159,7 +317,7 @@ gst_device_monitor_dispose (GObject * object)
 {
   GstDeviceMonitor *self = GST_DEVICE_MONITOR (object);
 
-  g_return_if_fail (self->priv->started == FALSE);
+  g_return_if_fail (!self->priv->started);
 
   if (self->priv->providers) {
     while (self->priv->providers->len)
@@ -168,8 +326,11 @@ gst_device_monitor_dispose (GObject * object)
     self->priv->providers = NULL;
   }
 
-  gst_caps_replace (&self->priv->caps, NULL);
-  g_free (self->priv->classes);
+  if (self->priv->filters) {
+    g_ptr_array_unref (self->priv->filters);
+    self->priv->filters = NULL;
+  }
+
   gst_object_replace ((GstObject **) & self->priv->bus, NULL);
 
   G_OBJECT_CLASS (gst_device_monitor_parent_class)->dispose (object);
@@ -182,7 +343,7 @@ gst_device_monitor_dispose (GObject * object)
  * Gets a list of devices from all of the relevant monitors. This may actually
  * probe the hardware if the monitor is not currently started.
  *
- * Returns: (transfer full) (element-type GstDevice): a #GList of
+ * Returns: (transfer full) (element-type GstDevice) (nullable): a #GList of
  *   #GstDevice
  *
  * Since: 1.4
@@ -191,7 +352,7 @@ gst_device_monitor_dispose (GObject * object)
 GList *
 gst_device_monitor_get_devices (GstDeviceMonitor * monitor)
 {
-  GList *devices = NULL;
+  GList *devices = NULL, *hidden = NULL;
   guint i;
   guint cookie;
 
@@ -199,10 +360,24 @@ gst_device_monitor_get_devices (GstDeviceMonitor * monitor)
 
   GST_OBJECT_LOCK (monitor);
 
+  if (monitor->priv->filters->len == 0) {
+    GST_OBJECT_UNLOCK (monitor);
+    GST_WARNING_OBJECT (monitor, "No filters have been set");
+    return NULL;
+  }
+
+  if (monitor->priv->providers->len == 0) {
+    GST_OBJECT_UNLOCK (monitor);
+    GST_WARNING_OBJECT (monitor, "No providers match the current filters");
+    return NULL;
+  }
+
 again:
 
   g_list_free_full (devices, gst_object_unref);
+  g_list_free_full (hidden, g_free);
   devices = NULL;
+  hidden = NULL;
 
   cookie = monitor->priv->cookie;
 
@@ -212,32 +387,47 @@ again:
         gst_object_ref (g_ptr_array_index (monitor->priv->providers, i));
     GList *item;
 
-    GST_OBJECT_UNLOCK (monitor);
+    if (!is_provider_hidden (monitor, hidden, provider)) {
+      GST_OBJECT_UNLOCK (monitor);
+
+      tmpdev = gst_device_provider_get_devices (provider);
+
+      GST_OBJECT_LOCK (monitor);
+      update_hidden_providers_list (&hidden, provider);
+    } else {
+      tmpdev = NULL;
+    }
 
-    tmpdev = gst_device_provider_get_devices (provider);
 
     for (item = tmpdev; item; item = item->next) {
       GstDevice *dev = GST_DEVICE (item->data);
       GstCaps *caps = gst_device_get_caps (dev);
-
-      if (gst_caps_can_intersect (monitor->priv->caps, caps) &&
-          gst_device_has_classes (dev, monitor->priv->classes))
-        devices = g_list_prepend (devices, gst_object_ref (dev));
+      guint j;
+
+      for (j = 0; j < monitor->priv->filters->len; j++) {
+        struct DeviceFilter *filter =
+            g_ptr_array_index (monitor->priv->filters, j);
+
+        if (gst_caps_can_intersect (filter->caps, caps) &&
+            gst_device_has_classesv (dev, filter->classesv)) {
+          devices = g_list_prepend (devices, gst_object_ref (dev));
+          break;
+        }
+      }
       gst_caps_unref (caps);
     }
 
     g_list_free_full (tmpdev, gst_object_unref);
     gst_object_unref (provider);
 
-    GST_OBJECT_LOCK (monitor);
-
     if (monitor->priv->cookie != cookie)
       goto again;
   }
+  g_list_free_full (hidden, g_free);
 
   GST_OBJECT_UNLOCK (monitor);
 
-  return devices;
+  return g_list_reverse (devices);
 }
 
 /**
@@ -256,37 +446,96 @@ again:
 gboolean
 gst_device_monitor_start (GstDeviceMonitor * monitor)
 {
-  guint i;
+  guint cookie, i;
+  GList *pending = NULL, *started = NULL, *removed = NULL;
 
   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
 
   GST_OBJECT_LOCK (monitor);
 
+  if (monitor->priv->filters->len == 0) {
+    GST_OBJECT_UNLOCK (monitor);
+    GST_WARNING_OBJECT (monitor, "No filters have been set, will expose all "
+        "devices found");
+    gst_device_monitor_add_filter (monitor, NULL, NULL);
+    GST_OBJECT_LOCK (monitor);
+  }
+
   if (monitor->priv->providers->len == 0) {
     GST_OBJECT_UNLOCK (monitor);
+    GST_WARNING_OBJECT (monitor, "No providers match the current filters");
     return FALSE;
   }
 
   gst_bus_set_flushing (monitor->priv->bus, FALSE);
 
+again:
+  cookie = monitor->priv->cookie;
+
+  g_list_free_full (pending, gst_object_unref);
+  pending = NULL;
+  removed = started;
+  started = NULL;
+
   for (i = 0; i < monitor->priv->providers->len; i++) {
-    if (!gst_device_provider_start (g_ptr_array_index (monitor->priv->providers,
-                i))) {
-      gst_bus_set_flushing (monitor->priv->bus, TRUE);
+    GstDeviceProvider *provider;
+    GList *find;
 
-      for (; i != 0; i--)
-        gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers,
-                i - 1));
+    provider = g_ptr_array_index (monitor->priv->providers, i);
 
-      GST_OBJECT_UNLOCK (monitor);
-      return FALSE;
+    find = g_list_find (removed, provider);
+    if (find) {
+      /* this was already started, move to started list */
+      removed = g_list_remove_link (removed, find);
+      started = g_list_concat (started, find);
+    } else {
+      /* not started, add to pending list */
+      pending = g_list_append (pending, gst_object_ref (provider));
     }
   }
+  g_list_free_full (removed, gst_object_unref);
+  removed = NULL;
+
+  while (pending) {
+    GstDeviceProvider *provider = pending->data;
+
+    if (gst_device_provider_can_monitor (provider)) {
+      GST_OBJECT_UNLOCK (monitor);
+
+      if (!gst_device_provider_start (provider))
+        goto start_failed;
+
+      GST_OBJECT_LOCK (monitor);
+    }
+    started = g_list_prepend (started, provider);
+    pending = g_list_delete_link (pending, pending);
 
+    if (monitor->priv->cookie != cookie)
+      goto again;
+  }
   monitor->priv->started = TRUE;
   GST_OBJECT_UNLOCK (monitor);
 
+  g_list_free_full (started, gst_object_unref);
+
   return TRUE;
+
+start_failed:
+  {
+    GST_OBJECT_LOCK (monitor);
+    gst_bus_set_flushing (monitor->priv->bus, TRUE);
+    GST_OBJECT_UNLOCK (monitor);
+
+    while (started) {
+      GstDeviceProvider *provider = started->data;
+
+      gst_device_provider_stop (provider);
+      gst_object_unref (provider);
+
+      started = g_list_delete_link (started, started);
+    }
+    return FALSE;
+  }
 }
 
 /**
@@ -301,200 +550,362 @@ void
 gst_device_monitor_stop (GstDeviceMonitor * monitor)
 {
   guint i;
+  GList *started = NULL;
 
   g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
 
   gst_bus_set_flushing (monitor->priv->bus, TRUE);
 
   GST_OBJECT_LOCK (monitor);
-  for (i = 0; i < monitor->priv->providers->len; i++)
-    gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers, i));
+  for (i = 0; i < monitor->priv->providers->len; i++) {
+    GstDeviceProvider *provider =
+        g_ptr_array_index (monitor->priv->providers, i);
+
+    started = g_list_prepend (started, gst_object_ref (provider));
+  }
+  GST_OBJECT_UNLOCK (monitor);
+
+  while (started) {
+    GstDeviceProvider *provider = started->data;
+
+    if (gst_device_provider_can_monitor (provider))
+      gst_device_provider_stop (provider);
+
+    started = g_list_delete_link (started, started);
+    gst_object_unref (provider);
+  }
+
+  GST_OBJECT_LOCK (monitor);
   monitor->priv->started = FALSE;
   GST_OBJECT_UNLOCK (monitor);
 
 }
 
+static void
+provider_hidden (GstDeviceProvider * provider, const gchar * hidden,
+    GstDeviceMonitor * monitor)
+{
+  GST_OBJECT_LOCK (monitor);
+  monitor->priv->hidden =
+      g_list_prepend (monitor->priv->hidden, g_strdup (hidden));
+  GST_OBJECT_UNLOCK (monitor);
+}
+
+static void
+provider_unhidden (GstDeviceProvider * provider, const gchar * hidden,
+    GstDeviceMonitor * monitor)
+{
+  GList *find;
+
+  GST_OBJECT_LOCK (monitor);
+  find =
+      g_list_find_custom (monitor->priv->hidden, hidden,
+      (GCompareFunc) g_strcmp0);
+  if (find) {
+    g_free (find->data);
+    monitor->priv->hidden = g_list_delete_link (monitor->priv->hidden, find);
+  }
+  GST_OBJECT_UNLOCK (monitor);
+}
+
 /**
- * gst_device_monitor_set_classes_filter:
- * @monitor: the device monitor
- * @classes: device classes to use as filter
+ * gst_device_monitor_add_filter:
+ * @monitor: a device monitor
+ * @classes: (allow-none): device classes to use as filter or %NULL for any class
+ * @caps: (allow-none): the #GstCaps to filter or %NULL for ANY
+ *
+ * Adds a filter for which #GstDevice will be monitored, any device that matches
+ * all these classes and the #GstCaps will be returned.
+ *
+ * If this function is called multiple times to add more filters, each will be
+ * matched independently. That is, adding more filters will not further restrict
+ * what devices are matched.
+ *
+ * The #GstCaps supported by the device as returned by gst_device_get_caps() are
+ * not intersected with caps filters added using this function.
  *
- * Filter devices monitored by device class, e.g. in case you are only
- * interested in a certain type of device like audio devices or
- * video sources.
+ * Filters must be added before the #GstDeviceMonitor is started.
+ *
+ * Returns: The id of the new filter or 0 if no provider matched the filter's
+ *  classes.
  *
  * Since: 1.4
  */
-void
-gst_device_monitor_set_classes_filter (GstDeviceMonitor * monitor,
-    const gchar * classes)
+guint
+gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
+    const gchar * classes, GstCaps * caps)
 {
   GList *factories = NULL;
-  guint i;
+  struct DeviceFilter *filter;
+  guint id = 0;
+  gboolean matched = FALSE;
 
-  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
-  g_return_if_fail (!monitor->priv->started);
+  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
+  g_return_val_if_fail (!monitor->priv->started, 0);
 
   GST_OBJECT_LOCK (monitor);
-  if (!strcmp (monitor->priv->classes, classes)) {
-    GST_OBJECT_UNLOCK (monitor);
-    return;
-  }
 
-  g_free (monitor->priv->classes);
-  monitor->priv->classes = g_strdup (classes);
+  filter = g_slice_new0 (struct DeviceFilter);
+  filter->id = monitor->priv->last_id++;
+  if (caps)
+    filter->caps = gst_caps_ref (caps);
+  else
+    filter->caps = gst_caps_new_any ();
+  if (classes)
+    filter->classesv = g_strsplit (classes, "/", 0);
 
-  factories = gst_device_provider_factory_list_get_device_providers (classes,
-      1);
+  factories = gst_device_provider_factory_list_get_device_providers (1);
 
-  for (i = 0; i < monitor->priv->providers->len; i++) {
-    GstDeviceProvider *provider;
-    GstDeviceProviderFactory *f;
-    GList *item;
+  while (factories) {
+    GstDeviceProviderFactory *factory = factories->data;
 
-    provider = g_ptr_array_index (monitor->priv->providers, i);
-    f = gst_device_provider_get_factory (provider);
+    if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
+      GstDeviceProvider *provider;
+
+      provider = gst_device_provider_factory_get (factory);
+
+      if (provider) {
+        guint i;
+
+        for (i = 0; i < monitor->priv->providers->len; i++) {
+          if (g_ptr_array_index (monitor->priv->providers, i) == provider) {
+            gst_object_unref (provider);
+            provider = NULL;
+            matched = TRUE;
+            break;
+          }
+        }
+      }
+
+      if (provider) {
+        GstBus *bus = gst_device_provider_get_bus (provider);
+
+        update_hidden_providers_list (&monitor->priv->hidden, provider);
+        g_signal_connect (provider, "provider-hidden",
+            (GCallback) provider_hidden, monitor);
+        g_signal_connect (provider, "provider-unhidden",
+            (GCallback) provider_unhidden, monitor);
+
+        matched = TRUE;
+        gst_bus_enable_sync_message_emission (bus);
+        g_signal_connect (bus, "sync-message",
+            G_CALLBACK (bus_sync_message), monitor);
+        gst_object_unref (bus);
+        g_ptr_array_add (monitor->priv->providers, provider);
+        monitor->priv->cookie++;
+      }
+    }
 
-    item = g_list_find (factories, f);
+    factories = g_list_remove (factories, factory);
+    gst_object_unref (factory);
+  }
 
-    if (item) {
-      /* If the item is in our list, then remove it from the list of factories,
-       * we don't have it to re-create it later
-       */
-      factories = g_list_remove_link (factories, item);
-      gst_object_unref (f);
-    } else {
-      /* If it's not in our list, them remove it from the list of providers.
-       */
+  /* Ensure there is no leak here */
+  g_assert (factories == NULL);
 
-      monitor->priv->cookie++;
-      gst_device_monitor_remove (monitor, i);
-      i--;
-    }
-  }
+  if (matched)
+    id = filter->id;
+  g_ptr_array_add (monitor->priv->filters, filter);
 
-  while (factories) {
-    GstDeviceProviderFactory *factory = factories->data;
-    GstDeviceProvider *provider;
+  GST_OBJECT_UNLOCK (monitor);
 
-    factories = g_list_remove (factories, factory);
+  return id;
+}
+
+/**
+ * gst_device_monitor_remove_filter:
+ * @monitor: a device monitor
+ * @filter_id: the id of the filter
+ *
+ * Removes a filter from the #GstDeviceMonitor using the id that was returned
+ * by gst_device_monitor_add_filter().
+ *
+ * Returns: %TRUE of the filter id was valid, %FALSE otherwise
+ *
+ * Since: 1.4
+ */
+gboolean
+gst_device_monitor_remove_filter (GstDeviceMonitor * monitor, guint filter_id)
+{
+  guint i, j;
+  gboolean removed = FALSE;
 
-    provider = gst_device_provider_factory_get (factory);
-    if (provider) {
-      GstBus *bus = gst_device_provider_get_bus (provider);
+  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
+  g_return_val_if_fail (!monitor->priv->started, FALSE);
+  g_return_val_if_fail (filter_id > 0, FALSE);
+
+  GST_OBJECT_LOCK (monitor);
+  for (i = 0; i < monitor->priv->filters->len; i++) {
+    struct DeviceFilter *filter = g_ptr_array_index (monitor->priv->filters, i);
 
-      gst_bus_enable_sync_message_emission (bus);
-      g_signal_connect (bus, "sync-message",
-          G_CALLBACK (bus_sync_message), monitor);
-      gst_object_unref (bus);
-      g_ptr_array_add (monitor->priv->providers, provider);
-      monitor->priv->cookie++;
+    if (filter->id == filter_id) {
+      g_ptr_array_remove_index (monitor->priv->filters, i);
+      removed = TRUE;
+      break;
     }
+  }
 
-    gst_object_unref (factory);
+  if (removed) {
+    for (i = 0; i < monitor->priv->providers->len; i++) {
+      GstDeviceProvider *provider =
+          g_ptr_array_index (monitor->priv->providers, i);
+      GstDeviceProviderFactory *factory =
+          gst_device_provider_get_factory (provider);
+      gboolean valid = FALSE;
+
+      for (j = 0; j < monitor->priv->filters->len; j++) {
+        struct DeviceFilter *filter =
+            g_ptr_array_index (monitor->priv->filters, j);
+
+        if (gst_device_provider_factory_has_classesv (factory,
+                filter->classesv)) {
+          valid = TRUE;
+          break;
+        }
+      }
+
+      if (!valid) {
+        monitor->priv->cookie++;
+        gst_device_monitor_remove (monitor, i);
+        i--;
+      }
+    }
   }
 
   GST_OBJECT_UNLOCK (monitor);
+
+  return removed;
 }
 
+
+
 /**
- * gst_device_monitor_get_classes_filter:
- * @monitor: the device monitor
+ * gst_device_monitor_new:
  *
- * Return the type (device classes) filter active for device filtering.
+ * Create a new #GstDeviceMonitor
  *
- * Returns: string of device classes that are being filtered.
+ * Returns: (transfer full): a new device monitor.
  *
  * Since: 1.4
  */
-gchar *
-gst_device_monitor_get_classes_filter (GstDeviceMonitor * monitor)
+GstDeviceMonitor *
+gst_device_monitor_new (void)
 {
-  gchar *res;
+  GstDeviceMonitor *monitor;
 
-  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
+  monitor = g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
 
-  GST_OBJECT_LOCK (monitor);
-  res = g_strdup (monitor->priv->classes);
-  GST_OBJECT_UNLOCK (monitor);
+  /* Clear floating flag */
+  gst_object_ref_sink (monitor);
 
-  return res;
+  return monitor;
 }
 
 /**
- * gst_device_monitor_set_caps_filter:
- * @monitor: the device monitor
- * @caps: caps to filter
+ * gst_device_monitor_get_bus:
+ * @monitor: a #GstDeviceProvider
  *
- * Set caps to use as filter for devices. By default ANY caps are used,
- * meaning no caps filter is active.
+ * Gets the #GstBus of this #GstDeviceMonitor
+ *
+ * Returns: (transfer full): a #GstBus
  *
  * Since: 1.4
  */
-void
-gst_device_monitor_set_caps_filter (GstDeviceMonitor * monitor, GstCaps * caps)
+GstBus *
+gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
 {
-  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
-  g_return_if_fail (GST_IS_CAPS (caps));
+  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
 
-  GST_OBJECT_LOCK (monitor);
-  gst_caps_replace (&monitor->priv->caps, caps);
-  GST_OBJECT_UNLOCK (monitor);
+  return gst_object_ref (monitor->priv->bus);
 }
 
 /**
- * gst_device_monitor_get_caps_filter:
- * @monitor: a device monitor
+ * gst_device_monitor_get_providers:
+ * @monitor: a #GstDeviceMonitor
  *
- * Get the #GstCaps filter set by gst_device_monitor_set_caps_filter().
+ * Get a list of the currently selected device provider factories.
  *
- * Returns: (transfer full): the filter caps that are active (or ANY caps)
+ * This
  *
- * Since: 1.4
+ * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*):
+ *     A list of device provider factory names that are currently being
+ *     monitored by @monitor or %NULL when nothing is being monitored.
+ *
+ * Since: 1.6
  */
-GstCaps *
-gst_device_monitor_get_caps_filter (GstDeviceMonitor * monitor)
+gchar **
+gst_device_monitor_get_providers (GstDeviceMonitor * monitor)
 {
-  GstCaps *res;
+  guint i, len;
+  gchar **res = NULL;
 
   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
 
   GST_OBJECT_LOCK (monitor);
-  res = gst_caps_ref (monitor->priv->caps);
+  len = monitor->priv->providers->len;
+  if (len == 0)
+    goto done;
+
+  res = g_new (gchar *, len + 1);
+
+  for (i = 0; i < len; i++) {
+    GstDeviceProvider *provider =
+        g_ptr_array_index (monitor->priv->providers, i);
+    GstDeviceProviderFactory *factory =
+        gst_device_provider_get_factory (provider);
+
+    res[i] = g_strdup (GST_OBJECT_NAME (factory));
+  }
+  res[i] = NULL;
+
+done:
   GST_OBJECT_UNLOCK (monitor);
 
   return res;
 }
 
 /**
- * gst_device_monitor_new:
- *
- * Create a new #GstDeviceMonitor
+ * gst_device_monitor_set_show_all_devices:
+ * @monitor: a #GstDeviceMonitor
+ * @show_all: show all devices
  *
- * Returns: (transfer full): a new device monitor.
+ * Set if all devices should be visible, even those devices from hidden
+ * providers. Setting @show_all to true might show some devices multiple times.
  *
- * Since: 1.4
+ * Since: 1.6
  */
-GstDeviceMonitor *
-gst_device_monitor_new (void)
+void
+gst_device_monitor_set_show_all_devices (GstDeviceMonitor * monitor,
+    gboolean show_all)
 {
-  return g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
+  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
+
+  GST_OBJECT_LOCK (monitor);
+  monitor->priv->show_all = show_all;
+  GST_OBJECT_UNLOCK (monitor);
 }
 
 /**
- * gst_device_monitor_get_bus:
- * @monitor: a #GstDeviceProvider
+ * gst_device_monitor_get_show_all_devices:
+ * @monitor: a #GstDeviceMonitor
  *
- * Gets the #GstBus of this #GstDeviceMonitor
+ * Get if @monitor is curretly showing all devices, even those from hidden
+ * providers.
  *
- * Returns: (transfer full): a #GstBus
+ * Returns: %TRUE when all devices will be shown.
  *
- * Since: 1.4
+ * Since: 1.6
  */
-GstBus *
-gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
+gboolean
+gst_device_monitor_get_show_all_devices (GstDeviceMonitor * monitor)
 {
-  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
+  gboolean res;
 
-  return gst_object_ref (monitor->priv->bus);
+  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
+
+  GST_OBJECT_LOCK (monitor);
+  res = monitor->priv->show_all;
+  GST_OBJECT_UNLOCK (monitor);
+
+  return res;
 }