/* GStreamer * Copyright (C) 2013 Olivier Crete * * gstglobaldevicemonitor.c: Global device monitor * * 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 "gst_private.h" #include "gstglobaldevicemonitor.h" struct _GstGlobalDeviceMonitorPrivate { gboolean started; GstBus *bus; GPtrArray *monitors; guint cookie; GstCaps *caps; gchar *classes; }; G_DEFINE_TYPE (GstGlobalDeviceMonitor, gst_global_device_monitor, GST_TYPE_OBJECT); static void gst_global_device_monitor_dispose (GObject * object); static void gst_global_device_monitor_class_init (GstGlobalDeviceMonitorClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (GstGlobalDeviceMonitorPrivate)); object_class->dispose = gst_global_device_monitor_dispose; } static void bus_sync_message (GstBus * bus, GstMessage * message, GstGlobalDeviceMonitor * monitor) { GstMessageType type = GST_MESSAGE_TYPE (message); if (type == GST_MESSAGE_DEVICE_ADDED || type == GST_MESSAGE_DEVICE_REMOVED) { gboolean matches; GstCaps *caps; GstDevice *device; if (type == GST_MESSAGE_DEVICE_ADDED) gst_message_parse_device_added (message, &device); else gst_message_parse_device_removed (message, &device); 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); GST_OBJECT_UNLOCK (monitor); if (matches) gst_bus_post (monitor->priv->bus, gst_message_ref (message)); } } static void gst_global_device_monitor_init (GstGlobalDeviceMonitor * self) { GList *factories; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_GLOBAL_DEVICE_MONITOR, GstGlobalDeviceMonitorPrivate); self->priv->bus = gst_bus_new (); gst_bus_set_flushing (self->priv->bus, TRUE); self->priv->monitors = g_ptr_array_new (); self->priv->caps = gst_caps_new_any (); self->priv->classes = g_strdup (""); factories = gst_device_monitor_factory_list_get_device_monitors (self->priv->classes, 1); while (factories) { GstDeviceMonitorFactory *factory = factories->data; GstDeviceMonitor *monitor; factories = g_list_remove (factories, factory); monitor = gst_device_monitor_factory_get (factory); if (monitor) { GstBus *bus = gst_device_monitor_get_bus (monitor); gst_bus_enable_sync_message_emission (bus); g_signal_connect (monitor, "sync-message", G_CALLBACK (bus_sync_message), self); g_ptr_array_add (self->priv->monitors, monitor); } gst_object_unref (factory); } } static void gst_global_device_monitor_remove (GstGlobalDeviceMonitor * self, guint i) { GstDeviceMonitor *monitor = g_ptr_array_index (self->priv->monitors, i); GstBus *bus; g_ptr_array_remove_index_fast (self->priv->monitors, i); bus = gst_device_monitor_get_bus (monitor); g_signal_handlers_disconnect_by_func (bus, bus_sync_message, self); gst_object_unref (bus); gst_object_unref (monitor); } static void gst_global_device_monitor_dispose (GObject * object) { GstGlobalDeviceMonitor *self = GST_GLOBAL_DEVICE_MONITOR (object); g_return_if_fail (self->priv->started == FALSE); if (self->priv->monitors) { while (self->priv->monitors->len) gst_global_device_monitor_remove (self, self->priv->monitors->len - 1); g_ptr_array_unref (self->priv->monitors); self->priv->monitors = NULL; } gst_caps_replace (&self->priv->caps, NULL); g_free (self->priv->classes); gst_object_replace ((GstObject **) & self->priv->bus, NULL); G_OBJECT_CLASS (gst_global_device_monitor_parent_class)->dispose (object); } /** * gst_global_device_monitor_get_devices: * @monitor: A #GstDeviceMonitor * * Gets a list of devices from all of the relevant monitors. This may actually * probe the hardware if the global monitor is not currently started. * * Returns: (transfer full) (element-type GstDevice): a #GList of * #GstDevice */ GList * gst_global_device_monitor_get_devices (GstGlobalDeviceMonitor * self) { GList *devices = NULL; guint i; guint cookie; g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self), NULL); GST_OBJECT_LOCK (self); again: g_list_free_full (devices, gst_object_unref); devices = NULL; cookie = self->priv->cookie; for (i = 0; i < self->priv->monitors->len; i++) { GList *tmpdev; GstDeviceMonitor *monitor = gst_object_ref (g_ptr_array_index (self->priv->monitors, i)); GList *item; GST_OBJECT_UNLOCK (self); tmpdev = gst_device_monitor_get_devices (monitor); 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 (self->priv->caps, caps) && gst_device_has_classes (dev, self->priv->classes)) devices = g_list_prepend (devices, gst_object_ref (dev)); gst_caps_unref (caps); } g_list_free_full (tmpdev, gst_object_unref); gst_object_unref (monitor); GST_OBJECT_LOCK (self); if (self->priv->cookie != cookie) goto again; } GST_OBJECT_UNLOCK (self); return devices; } /** * gst_global_device_monitor_start: * @monitor: A #GstGlobalDeviceMonitor * * Starts monitoring the devices, one this has succeeded, the * #GstGlobalDeviceMonitor:added and #GstGlobalDeviceMonitor:removed * signals will be emitted when the list of devices changes. * * Returns: %TRUE if the device monitoring could be started */ gboolean gst_global_device_monitor_start (GstGlobalDeviceMonitor * self) { guint i; g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self), FALSE); GST_OBJECT_LOCK (self); if (self->priv->monitors->len == 0) { GST_OBJECT_UNLOCK (self); return FALSE; } gst_bus_set_flushing (self->priv->bus, FALSE); for (i = 0; i < self->priv->monitors->len; i++) { if (!gst_device_monitor_start (g_ptr_array_index (self->priv->monitors, i))) { gst_bus_set_flushing (self->priv->bus, TRUE); for (; i != 0; i--) gst_device_monitor_stop (g_ptr_array_index (self->priv->monitors, i - 1)); GST_OBJECT_UNLOCK (self); return FALSE; } } self->priv->started = TRUE; GST_OBJECT_UNLOCK (self); return TRUE; } /** * gst_global_device_monitor_stop: * @monitor: A #GstDeviceMonitor * * Stops monitoring the devices. */ void gst_global_device_monitor_stop (GstGlobalDeviceMonitor * self) { guint i; g_return_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self)); gst_bus_set_flushing (self->priv->bus, TRUE); GST_OBJECT_LOCK (self); for (i = 0; i < self->priv->monitors->len; i++) gst_device_monitor_stop (g_ptr_array_index (self->priv->monitors, i)); self->priv->started = FALSE; GST_OBJECT_UNLOCK (self); } void gst_global_device_monitor_set_classes_filter (GstGlobalDeviceMonitor * self, const gchar * classes) { GList *factories = NULL; guint i; g_return_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self)); g_return_if_fail (!self->priv->started); GST_OBJECT_LOCK (self); if (!strcmp (self->priv->classes, classes)) { GST_OBJECT_UNLOCK (self); return; } g_free (self->priv->classes); self->priv->classes = g_strdup (classes); factories = gst_device_monitor_factory_list_get_device_monitors (self->priv->classes, 1); for (i = 0; i < self->priv->monitors->len; i++) { GstDeviceMonitor *monitor = g_ptr_array_index (self->priv->monitors, i); GstDeviceMonitorFactory *f = gst_device_monitor_get_factory (monitor); GList *item; item = g_list_find (factories, f); 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 monitors. */ self->priv->cookie++; gst_global_device_monitor_remove (self, i); i--; } } while (factories) { GstDeviceMonitorFactory *factory = factories->data; GstDeviceMonitor *monitor; factories = g_list_remove (factories, factory); monitor = gst_device_monitor_factory_get (factory); if (monitor) { GstBus *bus = gst_device_monitor_get_bus (monitor); gst_bus_enable_sync_message_emission (bus); g_signal_connect (bus, "sync-message", G_CALLBACK (bus_sync_message), self); gst_object_unref (bus); g_ptr_array_add (self->priv->monitors, monitor); self->priv->cookie++; } gst_object_unref (factory); } GST_OBJECT_UNLOCK (self); } gchar * gst_global_device_monitor_get_classes_filter (GstGlobalDeviceMonitor * self) { gchar *res; g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self), 0); GST_OBJECT_LOCK (self); res = g_strdup (self->priv->classes); GST_OBJECT_UNLOCK (self); return res; } void gst_global_device_monitor_set_caps_filter (GstGlobalDeviceMonitor * self, GstCaps * caps) { g_return_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self)); g_return_if_fail (GST_IS_CAPS (caps)); GST_OBJECT_LOCK (self); gst_caps_replace (&self->priv->caps, caps); GST_OBJECT_UNLOCK (self); } GstCaps * gst_global_device_monitor_get_caps_filter (GstGlobalDeviceMonitor * self) { GstCaps *res; g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self), NULL); GST_OBJECT_LOCK (self); res = gst_caps_ref (self->priv->caps); GST_OBJECT_UNLOCK (self); return res; } GstGlobalDeviceMonitor * gst_global_device_monitor_new (void) { return g_object_new (GST_TYPE_GLOBAL_DEVICE_MONITOR, NULL); } /** * gst_global_device_monitor_get_bus: * @monitor: a #GstDeviceMonitor * * Gets the #GstBus of this #GstGlobalDeviceMonitor * * Returns: (transfer full): a #GstBus */ GstBus * gst_global_device_monitor_get_bus (GstGlobalDeviceMonitor * monitor) { g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (monitor), NULL); return gst_object_ref (monitor->priv->bus); }