gstdevicemonitor.c 16.8 KB
Newer Older
1 2 3
/* GStreamer
 * Copyright (C) 2013 Olivier Crete <olivier.crete@collabora.com>
 *
4
 * gstdevicemonitor.c: device monitor
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 */
21 22

/**
23 24
 * SECTION:gstdevicemonitor
 * @short_description: A device monitor and prober
25
 * @see_also: #GstDevice, #GstDeviceProvider
26
 *
27
 * Applications should create a #GstDeviceMonitor when they want
28
 * to probe, list and monitor devices of a specific type. The
29
 * #GstDeviceMonitor will create the appropriate
30
 * #GstDeviceProvider objects and manage them. It will then post
31 32 33
 * messages on its #GstBus for devices that have been added and
 * removed.
 *
34 35 36
 * The device monitor will monitor all devices matching the filters that
 * the application has set.
 *
37
 *
38
 * The basic use pattern of a device monitor is as follows:
39 40 41 42 43
 * |[
 *   static gboolean
 *   my_bus_func (GstBus * bus, GstMessage * message, gpointer user_data)
 *   {
 *      GstDevice *device;
44
 *      gchar *name;
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
 *
 *      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);
 *          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);
 *          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);
 *
78
 *      caps = gst_caps_new_empty_simple ("video/x-raw");
79 80 81 82 83 84 85 86 87
 *      gst_device_monitor_add_filter (monitor, "Video/Source", caps);
 *      gst_caps_unref (caps);
 *
 *      gst_device_monitor_start (monitor);
 *
 *      return monitor;
 *   }
 * ]|
 *
88 89 90 91
 * Since: 1.4
 */


92 93 94 95
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

96
#include "gst_private.h"
97
#include "gstdevicemonitor.h"
98

99
struct _GstDeviceMonitorPrivate
100 101 102 103 104
{
  gboolean started;

  GstBus *bus;

105
  GPtrArray *providers;
106 107
  guint cookie;

108 109 110
  GPtrArray *filters;

  guint last_id;
111 112 113
};


114
G_DEFINE_TYPE (GstDeviceMonitor, gst_device_monitor, GST_TYPE_OBJECT);
115

116
static void gst_device_monitor_dispose (GObject * object);
117

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
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);
}

135
static void
136
gst_device_monitor_class_init (GstDeviceMonitorClass * klass)
137 138 139
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

140
  g_type_class_add_private (klass, sizeof (GstDeviceMonitorPrivate));
141

142
  object_class->dispose = gst_device_monitor_dispose;
143 144 145 146
}

static void
bus_sync_message (GstBus * bus, GstMessage * message,
147
    GstDeviceMonitor * monitor)
148
{
149
  GstMessageType type = GST_MESSAGE_TYPE (message);
150

151
  if (type == GST_MESSAGE_DEVICE_ADDED || type == GST_MESSAGE_DEVICE_REMOVED) {
152
    gboolean matches;
153 154
    GstDevice *device;

155
    if (type == GST_MESSAGE_DEVICE_ADDED)
156 157 158 159 160
      gst_message_parse_device_added (message, &device);
    else
      gst_message_parse_device_removed (message, &device);

    GST_OBJECT_LOCK (monitor);
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    if (monitor->priv->filters->len) {
      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;
      }
    } else {
      matches = TRUE;
    }
179 180
    GST_OBJECT_UNLOCK (monitor);

181 182
    gst_object_unref (device);

183
    if (matches)
184 185 186 187 188 189
      gst_bus_post (monitor->priv->bus, gst_message_ref (message));
  }
}


static void
190
gst_device_monitor_init (GstDeviceMonitor * self)
191 192
{
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
193
      GST_TYPE_DEVICE_MONITOR, GstDeviceMonitorPrivate);
194 195 196 197

  self->priv->bus = gst_bus_new ();
  gst_bus_set_flushing (self->priv->bus, TRUE);

198
  self->priv->providers = g_ptr_array_new ();
199 200
  self->priv->filters = g_ptr_array_new_with_free_func (
      (GDestroyNotify) device_filter_free);
201

202
  self->priv->last_id = 1;
203 204 205 206
}


static void
207
gst_device_monitor_remove (GstDeviceMonitor * self, guint i)
208
{
209
  GstDeviceProvider *provider = g_ptr_array_index (self->priv->providers, i);
210 211
  GstBus *bus;

212
  g_ptr_array_remove_index (self->priv->providers, i);
213

214
  bus = gst_device_provider_get_bus (provider);
215 216 217
  g_signal_handlers_disconnect_by_func (bus, bus_sync_message, self);
  gst_object_unref (bus);

218
  gst_object_unref (provider);
219 220 221
}

static void
222
gst_device_monitor_dispose (GObject * object)
223
{
224
  GstDeviceMonitor *self = GST_DEVICE_MONITOR (object);
225

226
  g_return_if_fail (!self->priv->started);
227

228 229
  if (self->priv->providers) {
    while (self->priv->providers->len)
230
      gst_device_monitor_remove (self, self->priv->providers->len - 1);
231 232
    g_ptr_array_unref (self->priv->providers);
    self->priv->providers = NULL;
233 234
  }

235 236 237 238
  if (self->priv->filters) {
    g_ptr_array_unref (self->priv->filters);
    self->priv->filters = NULL;
  }
239

240 241
  gst_object_replace ((GstObject **) & self->priv->bus, NULL);

242
  G_OBJECT_CLASS (gst_device_monitor_parent_class)->dispose (object);
243 244 245
}

/**
246
 * gst_device_monitor_get_devices:
247
 * @monitor: A #GstDeviceProvider
248 249
 *
 * Gets a list of devices from all of the relevant monitors. This may actually
250
 * probe the hardware if the monitor is not currently started.
251 252 253
 *
 * Returns: (transfer full) (element-type GstDevice): a #GList of
 *   #GstDevice
254 255
 *
 * Since: 1.4
256 257 258
 */

GList *
259
gst_device_monitor_get_devices (GstDeviceMonitor * monitor)
260 261 262 263 264
{
  GList *devices = NULL;
  guint i;
  guint cookie;

265
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
266

267
  GST_OBJECT_LOCK (monitor);
268

269 270 271 272 273 274 275 276 277 278 279 280
  if (monitor->priv->filters->len == 0) {
    GST_OBJECT_UNLOCK (monitor);
    GST_WARNING_OBJECT (monitor, "No filters have been set");
    return FALSE;
  }

  if (monitor->priv->providers->len == 0) {
    GST_OBJECT_UNLOCK (monitor);
    GST_WARNING_OBJECT (monitor, "No providers match the current filters");
    return FALSE;
  }

281 282 283 284 285
again:

  g_list_free_full (devices, gst_object_unref);
  devices = NULL;

286
  cookie = monitor->priv->cookie;
287

288
  for (i = 0; i < monitor->priv->providers->len; i++) {
289
    GList *tmpdev;
290 291
    GstDeviceProvider *provider =
        gst_object_ref (g_ptr_array_index (monitor->priv->providers, i));
292 293
    GList *item;

294
    GST_OBJECT_UNLOCK (monitor);
295

296
    tmpdev = gst_device_provider_get_devices (provider);
297

298 299
    GST_OBJECT_LOCK (monitor);

300 301 302
    for (item = tmpdev; item; item = item->next) {
      GstDevice *dev = GST_DEVICE (item->data);
      GstCaps *caps = gst_device_get_caps (dev);
303 304 305 306 307 308 309 310 311 312 313
      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;
        }
      }
314 315 316 317
      gst_caps_unref (caps);
    }

    g_list_free_full (tmpdev, gst_object_unref);
318
    gst_object_unref (provider);
319 320


321
    if (monitor->priv->cookie != cookie)
322 323 324
      goto again;
  }

325
  GST_OBJECT_UNLOCK (monitor);
326

327
  return g_list_reverse (devices);
328 329 330
}

/**
331 332
 * gst_device_monitor_start:
 * @monitor: A #GstDeviceMonitor
333 334
 *
 * Starts monitoring the devices, one this has succeeded, the
335 336
 * %GST_MESSAGE_DEVICE_ADDED and %GST_MESSAGE_DEVICE_REMOVED messages
 * will be emitted on the bus when the list of devices changes.
337 338
 *
 * Returns: %TRUE if the device monitoring could be started
339 340
 *
 * Since: 1.4
341 342 343
 */

gboolean
344
gst_device_monitor_start (GstDeviceMonitor * monitor)
345
{
346 347
  guint cookie, i;
  GList *pending = NULL, *started = NULL, *removed = NULL;
348

349
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
350

351
  GST_OBJECT_LOCK (monitor);
352

353 354
  if (monitor->priv->filters->len == 0) {
    GST_OBJECT_UNLOCK (monitor);
355 356 357 358
    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);
359 360
  }

361
  if (monitor->priv->providers->len == 0) {
362
    GST_OBJECT_UNLOCK (monitor);
363
    GST_WARNING_OBJECT (monitor, "No providers match the current filters");
364 365 366
    return FALSE;
  }

367
  gst_bus_set_flushing (monitor->priv->bus, FALSE);
368

369 370 371 372 373 374 375 376
again:
  cookie = monitor->priv->cookie;

  g_list_free_full (pending, gst_object_unref);
  pending = NULL;
  removed = started;
  started = NULL;

377
  for (i = 0; i < monitor->priv->providers->len; i++) {
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    GstDeviceProvider *provider;
    GList *find;

    provider = g_ptr_array_index (monitor->priv->providers, i);

    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;
398 399

    if (gst_device_provider_can_monitor (provider)) {
400
      GST_OBJECT_UNLOCK (monitor);
401

402 403
      if (!gst_device_provider_start (provider))
        goto start_failed;
404

405
      GST_OBJECT_LOCK (monitor);
406
    }
407 408
    started = g_list_prepend (started, provider);
    pending = g_list_delete_link (pending, pending);
409

410 411 412
    if (monitor->priv->cookie != cookie)
      goto again;
  }
413 414
  monitor->priv->started = TRUE;
  GST_OBJECT_UNLOCK (monitor);
415

416 417
  g_list_free_full (started, gst_object_unref);

418
  return TRUE;
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435

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;
  }
436 437 438
}

/**
439
 * gst_device_monitor_stop:
440
 * @monitor: A #GstDeviceProvider
441 442
 *
 * Stops monitoring the devices.
443 444
 *
 * Since: 1.4
445 446
 */
void
447
gst_device_monitor_stop (GstDeviceMonitor * monitor)
448 449
{
  guint i;
450
  GList *started = NULL;
451

452
  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
453

454
  gst_bus_set_flushing (monitor->priv->bus, TRUE);
455

456
  GST_OBJECT_LOCK (monitor);
457 458 459 460
  for (i = 0; i < monitor->priv->providers->len; i++) {
    GstDeviceProvider *provider =
        g_ptr_array_index (monitor->priv->providers, i);

461 462 463 464 465 466 467
    started = g_list_prepend (started, gst_object_ref (provider));
  }
  GST_OBJECT_UNLOCK (monitor);

  while (started) {
    GstDeviceProvider *provider = started->data;

468 469
    if (gst_device_provider_can_monitor (provider))
      gst_device_provider_stop (provider);
470 471 472

    started = g_list_delete_link (started, started);
    gst_object_unref (provider);
473
  }
474 475

  GST_OBJECT_LOCK (monitor);
476 477
  monitor->priv->started = FALSE;
  GST_OBJECT_UNLOCK (monitor);
478 479 480

}

481
/**
482 483
 * gst_device_monitor_add_filter:
 * @monitor: a device monitor
484
 * @classes: (allow-none): device classes to use as filter or %NULL for any class
485
 * @caps: (allow-none): the #GstCaps to filter or %NULL for ANY
486
 *
487 488 489 490 491
 * Adds a filter for which #GstDevice will be monitored, any device that matches
 * all classes and the #GstCaps will be returned.
 *
 * Filters must be added before the #GstDeviceMonitor is started.
 *
Nicolas Dufresne's avatar
Nicolas Dufresne committed
492
 * Returns: The id of the new filter or 0 if no provider matched the filter's
493
 *  classes.
494 495 496
 *
 * Since: 1.4
 */
497 498 499
guint
gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
    const gchar * classes, GstCaps * caps)
500 501
{
  GList *factories = NULL;
502 503 504
  struct DeviceFilter *filter;
  guint id = 0;
  gboolean matched = FALSE;
505

506 507
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
  g_return_val_if_fail (!monitor->priv->started, 0);
508

509
  GST_OBJECT_LOCK (monitor);
510

511 512 513 514 515 516 517 518
  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);
519

520
  factories = gst_device_provider_factory_list_get_device_providers (1);
521

522 523
  while (factories) {
    GstDeviceProviderFactory *factory = factories->data;
524

525 526
    if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
      GstDeviceProvider *provider;
527

528
      provider = gst_device_provider_factory_get (factory);
529

530 531
      if (provider) {
        guint i;
532

533 534 535 536 537 538 539 540 541
        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;
          }
        }
      }
542

543 544
      if (provider) {
        GstBus *bus = gst_device_provider_get_bus (provider);
545

546 547 548 549 550 551 552 553
        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++;
      }
554 555
    }

556
    factories = g_list_remove (factories, factory);
557 558 559
    gst_object_unref (factory);
  }

560 561
  /* Ensure there is no leak here */
  g_assert (factories == NULL);
562

563 564 565 566 567 568
  if (matched) {
    id = filter->id;
    g_ptr_array_add (monitor->priv->filters, filter);
  } else {
    device_filter_free (filter);
  }
569

570
  GST_OBJECT_UNLOCK (monitor);
571

572
  return id;
573 574
}

575
/**
576
 * gst_device_monitor_remove_filter:
577
 * @monitor: a device monitor
578
 * @filter_id: the id of the filter
579
 *
580 581
 * Removes a filter from the #GstDeviceMonitor using the id that was returned
 * by gst_device_monitor_add_filter().
582
 *
583
 * Returns: %TRUE of the filter id was valid, %FALSE otherwise
584 585 586
 *
 * Since: 1.4
 */
587 588
gboolean
gst_device_monitor_remove_filter (GstDeviceMonitor * monitor, guint filter_id)
589
{
590 591
  guint i, j;
  gboolean removed = FALSE;
592

593 594 595
  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);
596

597
  GST_OBJECT_LOCK (monitor);
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  for (i = 0; i < monitor->priv->filters->len; i++) {
    struct DeviceFilter *filter = g_ptr_array_index (monitor->priv->filters, i);

    if (filter->id == filter_id) {
      g_ptr_array_remove_index (monitor->priv->filters, i);
      removed = TRUE;
      break;
    }
  }

  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--;
      }
    }
  }

635
  GST_OBJECT_UNLOCK (monitor);
636

637
  return removed;
638 639
}

640 641


642
/**
643
 * gst_device_monitor_new:
644
 *
645
 * Create a new #GstDeviceMonitor
646
 *
647
 * Returns: (transfer full): a new device monitor.
648 649 650
 *
 * Since: 1.4
 */
651 652
GstDeviceMonitor *
gst_device_monitor_new (void)
653
{
654
  return g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
655 656 657
}

/**
658
 * gst_device_monitor_get_bus:
659
 * @monitor: a #GstDeviceProvider
660
 *
661
 * Gets the #GstBus of this #GstDeviceMonitor
662 663
 *
 * Returns: (transfer full): a #GstBus
664 665
 *
 * Since: 1.4
666 667
 */
GstBus *
668
gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
669
{
670
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
671 672 673

  return gst_object_ref (monitor->priv->bus);
}