tee: Check for the removed pad flag also in the slow pushing path
[platform/upstream/gstreamer.git] / gst / gstdeviceprovider.c
index 995c2ff..c3d794e 100644 (file)
 
 /**
  * SECTION:gstdeviceprovider
+ * @title: GstDeviceProvider
  * @short_description: A device provider
  * @see_also: #GstDevice, #GstDeviceMonitor
  *
  * A #GstDeviceProvider subclass is provided by a plugin that handles devices
- * if there is a way to programatically list connected devices. It can also
+ * if there is a way to programmatically list connected devices. It can also
  * optionally provide updates to the list of connected devices.
  *
  * Each #GstDeviceProvider subclass is a singleton, a plugin should
@@ -55,19 +56,30 @@ struct _GstDeviceProviderPrivate
   GMutex start_lock;
 
   gboolean started_count;
+
+  GList *hidden_providers;
+};
+
+enum
+{
+  PROVIDER_HIDDEN,
+  PROVIDER_UNHIDDEN,
+  LAST_SIGNAL
 };
 
+static guint gst_device_provider_signals[LAST_SIGNAL] = { 0 };
+
 /* this is used in gstelementfactory.c:gst_element_register() */
 GQuark __gst_deviceproviderclass_factory = 0;
 
 static void gst_device_provider_class_init (GstDeviceProviderClass * klass);
 static void gst_device_provider_init (GstDeviceProvider * element);
 static void gst_device_provider_base_class_init (gpointer g_class);
-static void gst_device_provider_base_class_finalize (gpointer g_class);
 static void gst_device_provider_dispose (GObject * object);
 static void gst_device_provider_finalize (GObject * object);
 
 static gpointer gst_device_provider_parent_class = NULL;
+static gint private_offset = 0;
 
 GType
 gst_device_provider_get_type (void)
@@ -79,7 +91,7 @@ gst_device_provider_get_type (void)
     static const GTypeInfo element_info = {
       sizeof (GstDeviceProviderClass),
       gst_device_provider_base_class_init,
-      gst_device_provider_base_class_finalize,
+      NULL,                     /* base_class_finalize */
       (GClassInitFunc) gst_device_provider_class_init,
       NULL,
       NULL,
@@ -92,6 +104,9 @@ gst_device_provider_get_type (void)
     _type = g_type_register_static (GST_TYPE_OBJECT, "GstDeviceProvider",
         &element_info, G_TYPE_FLAG_ABSTRACT);
 
+    private_offset =
+        g_type_add_instance_private (_type, sizeof (GstDeviceProviderPrivate));
+
     __gst_deviceproviderclass_factory =
         g_quark_from_static_string ("GST_DEVICEPROVIDERCLASS_FACTORY");
     g_once_init_leave (&gst_device_provider_type, _type);
@@ -99,6 +114,12 @@ gst_device_provider_get_type (void)
   return gst_device_provider_type;
 }
 
+static inline gpointer
+gst_device_provider_get_instance_private (GstDeviceProvider * self)
+{
+  return (G_STRUCT_MEMBER_P (self, private_offset));
+}
+
 static void
 gst_device_provider_base_class_init (gpointer g_class)
 {
@@ -116,31 +137,33 @@ gst_device_provider_base_class_init (gpointer g_class)
 }
 
 static void
-gst_device_provider_base_class_finalize (gpointer g_class)
-{
-  GstDeviceProviderClass *klass = GST_DEVICE_PROVIDER_CLASS (g_class);
-
-  gst_structure_free (klass->metadata);
-}
-
-static void
 gst_device_provider_class_init (GstDeviceProviderClass * klass)
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
 
   gst_device_provider_parent_class = g_type_class_peek_parent (klass);
 
-  g_type_class_add_private (klass, sizeof (GstDeviceProviderPrivate));
+  if (private_offset != 0)
+    g_type_class_adjust_private_offset (klass, &private_offset);
 
   gobject_class->dispose = gst_device_provider_dispose;
   gobject_class->finalize = gst_device_provider_finalize;
+
+  gst_device_provider_signals[PROVIDER_HIDDEN] =
+      g_signal_new ("provider-hidden", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, 0, NULL,
+      NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING);
+
+  gst_device_provider_signals[PROVIDER_UNHIDDEN] =
+      g_signal_new ("provider-unhidden", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, 0, NULL,
+      NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING);
 }
 
 static void
 gst_device_provider_init (GstDeviceProvider * provider)
 {
-  provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider,
-      GST_TYPE_DEVICE_PROVIDER, GstDeviceProviderPrivate);
+  provider->priv = gst_device_provider_get_instance_private (provider);
 
   g_mutex_init (&provider->priv->start_lock);
 
@@ -240,7 +263,8 @@ gst_device_provider_class_add_static_metadata (GstDeviceProviderClass * klass,
  * multiple author metadata. E.g: "Joe Bloggs <joe.blogs at foo.com>"
  *
  * Sets the detailed information for a #GstDeviceProviderClass.
- * <note>This function is for use in _class_init functions only.</note>
+ *
+ * > This function is for use in _class_init functions only.
  *
  * Since: 1.4
  */
@@ -276,7 +300,8 @@ gst_device_provider_class_set_metadata (GstDeviceProviderClass * klass,
  * foo.com&gt;"
  *
  * Sets the detailed information for a #GstDeviceProviderClass.
- * <note>This function is for use in _class_init functions only.</note>
+ *
+ * > This function is for use in _class_init functions only.
  *
  * Same as gst_device_provider_class_set_metadata(), but @longname, @classification,
  * @description, and @author must be static strings or inlined strings, as
@@ -322,7 +347,7 @@ gst_device_provider_class_set_static_metadata (GstDeviceProviderClass * klass,
  *
  * Get metadata with @key in @klass.
  *
- * Returns: the metadata for @key.
+ * Returns: (nullable): the metadata for @key.
  *
  * Since: 1.4
  */
@@ -337,6 +362,29 @@ gst_device_provider_class_get_metadata (GstDeviceProviderClass * klass,
 }
 
 /**
+ * gst_device_provider_get_metadata:
+ * @provider: provider to get metadata for
+ * @key: the key to get
+ *
+ * Get metadata with @key in @provider.
+ *
+ * Returns: the metadata for @key.
+ *
+ * Since: 1.14
+ */
+const gchar *
+gst_device_provider_get_metadata (GstDeviceProvider * provider,
+    const gchar * key)
+{
+  g_return_val_if_fail (GST_IS_DEVICE_PROVIDER (provider), NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  return
+      gst_device_provider_class_get_metadata (GST_DEVICE_PROVIDER_GET_CLASS
+      (provider), key);
+}
+
+/**
  * gst_device_provider_get_devices:
  * @provider: A #GstDeviceProvider
  *
@@ -406,6 +454,7 @@ gst_device_provider_start (GstDeviceProvider * provider)
   g_mutex_lock (&provider->priv->start_lock);
 
   if (provider->priv->started_count > 0) {
+    provider->priv->started_count++;
     ret = TRUE;
     goto started;
   }
@@ -528,13 +577,16 @@ gst_device_provider_get_bus (GstDeviceProvider * provider)
 /**
  * gst_device_provider_device_add:
  * @provider: a #GstDeviceProvider
- * @device: (transfer full): a #GstDevice that has been added
+ * @device: (transfer floating): a #GstDevice that has been added
  *
  * Posts a message on the provider's #GstBus to inform applications that
  * a new device has been added.
  *
  * This is for use by subclasses.
  *
+ * @device's reference count will be incremented, and any floating reference
+ * will be removed (see gst_object_ref_sink()).
+ *
  * Since: 1.4
  */
 void
@@ -553,8 +605,10 @@ gst_device_provider_device_add (GstDeviceProvider * provider,
   }
 
   GST_OBJECT_LOCK (provider);
-  provider->devices = g_list_prepend (provider->devices,
-      gst_object_ref (device));
+  /* Take an additional reference so we can be sure nobody removed it from the
+   * provider in the meantime and we can safely emit the message */
+  gst_object_ref (device);
+  provider->devices = g_list_prepend (provider->devices, device);
   GST_OBJECT_UNLOCK (provider);
 
   message = gst_message_new_device_added (GST_OBJECT (provider), device);
@@ -598,3 +652,173 @@ gst_device_provider_device_remove (GstDeviceProvider * provider,
   if (item)
     gst_object_unparent (GST_OBJECT (device));
 }
+
+/**
+ * gst_device_provider_get_hidden_providers:
+ * @provider: a #GstDeviceProvider
+ *
+ * Get the provider factory names of the #GstDeviceProvider instances that
+ * are hidden by @provider.
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*):
+ *   a list of hidden providers factory names or %NULL when
+ *   nothing is hidden by @provider. Free with g_strfreev.
+ *
+ * Since: 1.6
+ */
+gchar **
+gst_device_provider_get_hidden_providers (GstDeviceProvider * provider)
+{
+  GList *walk;
+  guint i, len;
+  gchar **res = NULL;
+
+  g_return_val_if_fail (GST_IS_DEVICE_PROVIDER (provider), NULL);
+
+  GST_OBJECT_LOCK (provider);
+  len = g_list_length (provider->priv->hidden_providers);
+  if (len == 0)
+    goto done;
+
+  res = g_new (gchar *, len + 1);
+  for (i = 0, walk = provider->priv->hidden_providers; walk;
+      walk = g_list_next (walk), i++)
+    res[i] = g_strdup (walk->data);
+  res[i] = NULL;
+
+done:
+  GST_OBJECT_UNLOCK (provider);
+
+  return res;
+}
+
+/**
+ * gst_device_provider_hide_provider:
+ * @provider: a #GstDeviceProvider
+ * @name: a provider factory name
+ *
+ * Make @provider hide the devices from the factory with @name.
+ *
+ * This function is used when @provider will also provide the devices reported
+ * by provider factory @name. A monitor should stop monitoring the
+ * device provider with @name to avoid duplicate devices.
+ *
+ * Since: 1.6
+ */
+void
+gst_device_provider_hide_provider (GstDeviceProvider * provider,
+    const gchar * name)
+{
+  GList *find;
+  const gchar *hidden_name = NULL;
+
+  g_return_if_fail (GST_IS_DEVICE_PROVIDER (provider));
+  g_return_if_fail (name != NULL);
+
+  GST_OBJECT_LOCK (provider);
+  find =
+      g_list_find_custom (provider->priv->hidden_providers, name,
+      (GCompareFunc) g_strcmp0);
+  if (find == NULL) {
+    hidden_name = name;
+    provider->priv->hidden_providers =
+        g_list_prepend (provider->priv->hidden_providers, g_strdup (name));
+  }
+  GST_OBJECT_UNLOCK (provider);
+
+  if (hidden_name)
+    g_signal_emit (provider, gst_device_provider_signals[PROVIDER_HIDDEN],
+        0, hidden_name);
+}
+
+/**
+ * gst_device_provider_unhide_provider:
+ * @provider: a #GstDeviceProvider
+ * @name: a provider factory name
+ *
+ * Make @provider unhide the devices from factory @name.
+ *
+ * This function is used when @provider will no longer provide the devices
+ * reported by provider factory @name. A monitor should start
+ * monitoring the devices from provider factory @name in order to see
+ * all devices again.
+ *
+ * Since: 1.6
+ */
+void
+gst_device_provider_unhide_provider (GstDeviceProvider * provider,
+    const gchar * name)
+{
+  GList *find;
+  gchar *unhidden_name = NULL;
+
+  g_return_if_fail (GST_IS_DEVICE_PROVIDER (provider));
+  g_return_if_fail (name != NULL);
+
+  GST_OBJECT_LOCK (provider);
+  find =
+      g_list_find_custom (provider->priv->hidden_providers, name,
+      (GCompareFunc) g_strcmp0);
+  if (find) {
+    unhidden_name = find->data;
+    provider->priv->hidden_providers =
+        g_list_delete_link (provider->priv->hidden_providers, find);
+  }
+  GST_OBJECT_UNLOCK (provider);
+
+  if (unhidden_name) {
+    g_signal_emit (provider,
+        gst_device_provider_signals[PROVIDER_UNHIDDEN], 0, unhidden_name);
+    g_free (unhidden_name);
+  }
+}
+
+/**
+ * gst_device_provider_device_changed:
+ * @device: (transfer none): the new version of @changed_device
+ * @changed_device: (transfer floating): the old version of the device that has been udpated
+ *
+ * This function is used when @changed_device was modified into its new form
+ * @device. This will post a `DEVICE_CHANGED` message on the bus to let
+ * the application know that the device was modified. #GstDevice is immutable
+ * for MT. safety purposes so this is an "atomic" way of letting the application
+ * know when a device was modified.
+ *
+ * Since: 1.16
+ */
+void
+gst_device_provider_device_changed (GstDeviceProvider * provider,
+    GstDevice * device, GstDevice * changed_device)
+{
+  GList *dev_lst;
+  GstMessage *message;
+
+  g_return_if_fail (GST_IS_DEVICE_PROVIDER (provider));
+  g_return_if_fail (GST_IS_DEVICE (device));
+  g_return_if_fail (GST_IS_DEVICE (changed_device));
+
+  GST_OBJECT_LOCK (provider);
+  dev_lst = g_list_find (provider->devices, changed_device);
+  if (!dev_lst) {
+    GST_ERROR_OBJECT (provider,
+        "Trying to update a device we do not have in our own list!");
+
+    GST_OBJECT_UNLOCK (provider);
+    return;
+  }
+
+  if (!gst_object_set_parent (GST_OBJECT (device), GST_OBJECT (provider))) {
+    GST_OBJECT_UNLOCK (provider);
+    GST_WARNING_OBJECT (provider, "Could not parent device %p to provider,"
+        " it already has a parent", device);
+    return;
+  }
+  dev_lst->data = device;
+  GST_OBJECT_UNLOCK (provider);
+
+  message =
+      gst_message_new_device_changed (GST_OBJECT (provider), device,
+      changed_device);
+  gst_bus_post (provider->priv->bus, message);
+  gst_object_unparent (GST_OBJECT (changed_device));
+}