diff --git a/gst/Makefile.am b/gst/Makefile.am index af84844f47..2f69db169e 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -80,6 +80,7 @@ libgstreamer_@GST_API_VERSION@_la_SOURCES = \ gstevent.c \ gstformat.c \ gstghostpad.c \ + gstglobaldevicemonitor.c \ gstinfo.c \ gstiterator.c \ gstatomicqueue.c \ @@ -183,6 +184,7 @@ gst_headers = \ gstevent.h \ gstformat.h \ gstghostpad.h \ + gstglobaldevicemonitor.h \ gstinfo.h \ gstiterator.h \ gstatomicqueue.h \ diff --git a/gst/gst.h b/gst/gst.h index 5e24e1792f..9b20ba15d0 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/gst/gstglobaldevicemonitor.c b/gst/gstglobaldevicemonitor.c new file mode 100644 index 0000000000..bd4935801f --- /dev/null +++ b/gst/gstglobaldevicemonitor.c @@ -0,0 +1,428 @@ +/* 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 + +#include "gst/gst_private.h" +#include + +struct _GstGlobalDeviceMonitorPrivate +{ + gboolean started; + + GstBus *bus; + + GPtrArray *monitors; + guint cookie; + + GstCaps *caps; + GstDeviceMonitorFactoryListType type; +}; + + +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) +{ + + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EXTENDED && + (gst_message_get_extended_type (message) == GST_MESSAGE_DEVICE_ADDED || + gst_message_get_extended_type (message) == + GST_MESSAGE_DEVICE_REMOVED)) { + gboolean intersects; + GstCaps *caps; + GstDevice *device; + + if (gst_message_get_extended_type (message) == 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); + intersects = gst_caps_can_intersect (monitor->priv->caps, caps); + gst_caps_unref (caps); + GST_OBJECT_UNLOCK (monitor); + + if (intersects) + 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->type = GST_DEVICE_MONITOR_FACTORY_TYPE_SINK | + GST_DEVICE_MONITOR_FACTORY_TYPE_SRC; + + factories = + gst_device_monitor_factory_list_get_device_monitors (self->priv->type, 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::extended", + 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); + 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)) + 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_type_filter (GstGlobalDeviceMonitor * self, + GstDeviceMonitorFactoryListType type) +{ + GList *factories = NULL; + guint i; + + g_return_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self)); + g_return_if_fail (!self->priv->started); + g_return_if_fail (type & + (GST_DEVICE_MONITOR_FACTORY_TYPE_SINK | + GST_DEVICE_MONITOR_FACTORY_TYPE_SRC)); + + GST_OBJECT_LOCK (self); + if (self->priv->type == type) { + GST_OBJECT_UNLOCK (self); + return; + } + + self->priv->type = type; + + factories = + gst_device_monitor_factory_list_get_device_monitors (self->priv->type, 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::extended", + 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); +} + +GstDeviceMonitorFactoryListType +gst_global_device_monitor_get_type_filter (GstGlobalDeviceMonitor * self) +{ + GstDeviceMonitorFactoryListType res; + + g_return_val_if_fail (GST_IS_GLOBAL_DEVICE_MONITOR (self), 0); + + GST_OBJECT_LOCK (self); + res = self->priv->type; + 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); +} diff --git a/gst/gstglobaldevicemonitor.h b/gst/gstglobaldevicemonitor.h new file mode 100644 index 0000000000..2697028ea3 --- /dev/null +++ b/gst/gstglobaldevicemonitor.h @@ -0,0 +1,93 @@ +/* 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. + */ + + +#ifndef __GST_GLOBAL_DEVICE_MONITOR_H__ +#define __GST_GLOBAL_DEVICE_MONITOR_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstGlobalDeviceMonitor GstGlobalDeviceMonitor; +typedef struct _GstGlobalDeviceMonitorPrivate GstGlobalDeviceMonitorPrivate; +typedef struct _GstGlobalDeviceMonitorClass GstGlobalDeviceMonitorClass; + +#define GST_TYPE_GLOBAL_DEVICE_MONITOR (gst_global_device_monitor_get_type()) +#define GST_IS_GLOBAL_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GLOBAL_DEVICE_MONITOR)) +#define GST_IS_GLOBAL_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GLOBAL_DEVICE_MONITOR)) +#define GST_GLOBAL_DEVICE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GLOBAL_DEVICE_MONITOR, GstGlobalDeviceMonitorClass)) +#define GST_GLOBAL_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GLOBAL_DEVICE_MONITOR, GstGlobalDeviceMonitor)) +#define GST_GLOBAL_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GLOBAL_DEVICE_MONITOR, GstGlobalDeviceMonitorClass)) +#define GST_GLOBAL_DEVICE_MONITOR_CAST(obj) ((GstGlobalDeviceMonitor *)(obj)) + + +struct _GstGlobalDeviceMonitor { + GstObject parent; + + /*< private >*/ + + GstGlobalDeviceMonitorPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstGlobalDeviceMonitorClass { + GstObjectClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_global_device_monitor_get_type (void); + +GstGlobalDeviceMonitor * gst_global_device_monitor_new (void); + +GList * gst_global_device_monitor_get_devices (GstGlobalDeviceMonitor * monitor); + +gboolean gst_global_device_monitor_start (GstGlobalDeviceMonitor * monitor); +void gst_global_device_monitor_stop (GstGlobalDeviceMonitor * monitor); + + +void gst_global_device_monitor_set_type_filter ( + GstGlobalDeviceMonitor * monitor, + GstDeviceMonitorFactoryListType type); + +GstDeviceMonitorFactoryListType gst_global_device_monitor_get_type_filter ( + GstGlobalDeviceMonitor * monitor); + + +void gst_global_device_monitor_set_caps_filter ( + GstGlobalDeviceMonitor * monitor, + GstCaps * caps); + +GstCaps * gst_global_device_monitor_get_caps_filter ( + GstGlobalDeviceMonitor * monitor); + +GstBus * gst_global_device_monitor_get_bus ( + GstGlobalDeviceMonitor * monitor); + +G_END_DECLS + +#endif /* __GST_GLOBAL_DEVICE_MONITOR_H__ */ diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index 0df41c01ab..463039b068 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -581,6 +581,16 @@ EXPORTS gst_ghost_pad_new_no_target gst_ghost_pad_new_no_target_from_template gst_ghost_pad_set_target + gst_global_device_monitor_get_bus + gst_global_device_monitor_get_caps_filter + gst_global_device_monitor_get_devices + gst_global_device_monitor_get_type + gst_global_device_monitor_get_type_filter + gst_global_device_monitor_new + gst_global_device_monitor_set_caps_filter + gst_global_device_monitor_set_type_filter + gst_global_device_monitor_start + gst_global_device_monitor_stop gst_init gst_init_check gst_init_get_option_group