gstdevicemonitor.c 15.4 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 44 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 78 79 80 81 82 83 84 85 86 87
 * |[
 *   static gboolean
 *   my_bus_func (GstBus * bus, GstMessage * message, gpointer user_data)
 *   {
 *      GstDevice *device;
 *      gchar name;
 *
 *      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);
 *
 *      caps = gst_caps_new_simple_empty ("video/x-raw");
 *      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_fast (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 227

  g_return_if_fail (self->priv->started == FALSE);

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 328 329 330

  return devices;
}

/**
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 i;

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

350
  GST_OBJECT_LOCK (monitor);
351

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

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

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

368
  for (i = 0; i < monitor->priv->providers->len; i++) {
369 370 371 372 373 374
    GstDeviceProvider *provider =
        g_ptr_array_index (monitor->priv->providers, i);

    if (gst_device_provider_can_monitor (provider)) {
      if (!gst_device_provider_start (provider)) {
        gst_bus_set_flushing (monitor->priv->bus, TRUE);
375

376 377 378
        for (; i != 0; i--)
          gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers,
                  i - 1));
379

380 381 382
        GST_OBJECT_UNLOCK (monitor);
        return FALSE;
      }
383 384 385
    }
  }

386 387
  monitor->priv->started = TRUE;
  GST_OBJECT_UNLOCK (monitor);
388 389 390 391 392

  return TRUE;
}

/**
393
 * gst_device_monitor_stop:
394
 * @monitor: A #GstDeviceProvider
395 396
 *
 * Stops monitoring the devices.
397 398
 *
 * Since: 1.4
399 400
 */
void
401
gst_device_monitor_stop (GstDeviceMonitor * monitor)
402 403 404
{
  guint i;

405
  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
406

407
  gst_bus_set_flushing (monitor->priv->bus, TRUE);
408

409
  GST_OBJECT_LOCK (monitor);
410 411 412 413 414 415 416
  for (i = 0; i < monitor->priv->providers->len; i++) {
    GstDeviceProvider *provider =
        g_ptr_array_index (monitor->priv->providers, i);

    if (gst_device_provider_can_monitor (provider))
      gst_device_provider_stop (provider);
  }
417 418
  monitor->priv->started = FALSE;
  GST_OBJECT_UNLOCK (monitor);
419 420 421

}

422
/**
423 424
 * gst_device_monitor_add_filter:
 * @monitor: a device monitor
425
 * @classes: (allow-none): device classes to use as filter or %NULL for any class
426
 * @caps: (allow-none): the #GstCaps to filter or %NULL for ANY
427
 *
428 429 430 431 432 433 434
 * 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.
 *
 * Returns: The id of the new filter or %0 if no provider matched the filter's
 *  classes.
435 436 437
 *
 * Since: 1.4
 */
438 439 440
guint
gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
    const gchar * classes, GstCaps * caps)
441 442
{
  GList *factories = NULL;
443 444 445
  struct DeviceFilter *filter;
  guint id = 0;
  gboolean matched = FALSE;
446

447 448
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
  g_return_val_if_fail (!monitor->priv->started, 0);
449

450
  GST_OBJECT_LOCK (monitor);
451

452 453 454 455 456 457 458 459
  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);
460

461
  factories = gst_device_provider_factory_list_get_device_providers (1);
462

463 464
  while (factories) {
    GstDeviceProviderFactory *factory = factories->data;
465

466

467 468
    if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
      GstDeviceProvider *provider;
469

470
      provider = gst_device_provider_factory_get (factory);
471

472 473
      if (provider) {
        guint i;
474

475 476 477 478 479 480 481 482 483
        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;
          }
        }
      }
484

485 486
      if (provider) {
        GstBus *bus = gst_device_provider_get_bus (provider);
487

488 489 490 491 492 493 494 495
        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++;
      }
496 497
    }

498
    factories = g_list_remove (factories, factory);
499 500 501
    gst_object_unref (factory);
  }

502 503
  /* Ensure there is no leak here */
  g_assert (factories == NULL);
504

505 506 507 508 509 510
  if (matched) {
    id = filter->id;
    g_ptr_array_add (monitor->priv->filters, filter);
  } else {
    device_filter_free (filter);
  }
511

512
  GST_OBJECT_UNLOCK (monitor);
513

514
  return id;
515 516
}

517
/**
518
 * gst_device_monitor_remove_filter:
519
 * @monitor: a device monitor
520
 * @filter_id: the id of the filter
521
 *
522 523
 * Removes a filter from the #GstDeviceMonitor using the id that was returned
 * by gst_device_monitor_add_filter().
524
 *
525
 * Returns: %TRUE of the filter id was valid, %FALSE otherwise
526 527 528
 *
 * Since: 1.4
 */
529 530
gboolean
gst_device_monitor_remove_filter (GstDeviceMonitor * monitor, guint filter_id)
531
{
532 533
  guint i, j;
  gboolean removed = FALSE;
534

535 536 537
  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);
538

539
  GST_OBJECT_LOCK (monitor);
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
  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--;
      }
    }
  }

577
  GST_OBJECT_UNLOCK (monitor);
578

579
  return removed;
580 581
}

582 583


584
/**
585
 * gst_device_monitor_new:
586
 *
587
 * Create a new #GstDeviceMonitor
588
 *
589
 * Returns: (transfer full): a new device monitor.
590 591 592
 *
 * Since: 1.4
 */
593 594
GstDeviceMonitor *
gst_device_monitor_new (void)
595
{
596
  return g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
597 598 599
}

/**
600
 * gst_device_monitor_get_bus:
601
 * @monitor: a #GstDeviceProvider
602
 *
603
 * Gets the #GstBus of this #GstDeviceMonitor
604 605
 *
 * Returns: (transfer full): a #GstBus
606 607
 *
 * Since: 1.4
608 609
 */
GstBus *
610
gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
611
{
612
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
613 614 615

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