devicemonitor: do start and stop outside of the lock

Release the monitor lock when calling the provider start/stop methods.
Because we release the lock now, We need to make sure we check the
cookie again and keep track of started and removed providers.
diff --git a/gst/gstdevicemonitor.c b/gst/gstdevicemonitor.c
index 994263a..a07c6d8 100644
--- a/gst/gstdevicemonitor.c
+++ b/gst/gstdevicemonitor.c
@@ -343,7 +343,8 @@
 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);
 
@@ -365,28 +366,73 @@
 
   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++) {
-    GstDeviceProvider *provider =
-        g_ptr_array_index (monitor->priv->providers, i);
+    GstDeviceProvider *provider;
+    GList *find;
 
-    if (gst_device_provider_can_monitor (provider)) {
-      if (!gst_device_provider_start (provider)) {
-        gst_bus_set_flushing (monitor->priv->bus, TRUE);
+    provider = g_ptr_array_index (monitor->priv->providers, i);
 
-        for (; i != 0; i--)
-          gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers,
-                  i - 1));
-
-        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;
+  }
 }
 
 /**
@@ -401,6 +447,7 @@
 gst_device_monitor_stop (GstDeviceMonitor * monitor)
 {
   guint i;
+  GList *started = NULL;
 
   g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
 
@@ -411,9 +458,21 @@
     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);
 
@@ -463,7 +522,6 @@
   while (factories) {
     GstDeviceProviderFactory *factory = factories->data;
 
-
     if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
       GstDeviceProvider *provider;