gstdevicemonitor.c 15.3 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
  g_clear_pointer (&self->priv->filters, (GDestroyNotify) g_ptr_array_unref);

237 238
  gst_object_replace ((GstObject **) & self->priv->bus, NULL);

239
  G_OBJECT_CLASS (gst_device_monitor_parent_class)->dispose (object);
240 241 242
}

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

GList *
256
gst_device_monitor_get_devices (GstDeviceMonitor * monitor)
257 258 259 260 261
{
  GList *devices = NULL;
  guint i;
  guint cookie;

262
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
263

264
  GST_OBJECT_LOCK (monitor);
265

266 267 268 269 270 271 272 273 274 275 276 277
  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;
  }

278 279 280 281 282
again:

  g_list_free_full (devices, gst_object_unref);
  devices = NULL;

283
  cookie = monitor->priv->cookie;
284

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

291
    GST_OBJECT_UNLOCK (monitor);
292

293
    tmpdev = gst_device_provider_get_devices (provider);
294

295 296
    GST_OBJECT_LOCK (monitor);

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

    g_list_free_full (tmpdev, gst_object_unref);
315
    gst_object_unref (provider);
316 317


318
    if (monitor->priv->cookie != cookie)
319 320 321
      goto again;
  }

322
  GST_OBJECT_UNLOCK (monitor);
323 324 325 326 327

  return devices;
}

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

gboolean
341
gst_device_monitor_start (GstDeviceMonitor * monitor)
342 343 344
{
  guint i;

345
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
346

347
  GST_OBJECT_LOCK (monitor);
348

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

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

363
  gst_bus_set_flushing (monitor->priv->bus, FALSE);
364

365
  for (i = 0; i < monitor->priv->providers->len; i++) {
366 367 368 369 370 371
    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);
372

373 374 375
        for (; i != 0; i--)
          gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers,
                  i - 1));
376

377 378 379
        GST_OBJECT_UNLOCK (monitor);
        return FALSE;
      }
380 381 382
    }
  }

383 384
  monitor->priv->started = TRUE;
  GST_OBJECT_UNLOCK (monitor);
385 386 387 388 389

  return TRUE;
}

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

402
  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
403

404
  gst_bus_set_flushing (monitor->priv->bus, TRUE);
405

406
  GST_OBJECT_LOCK (monitor);
407 408 409 410 411 412 413
  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);
  }
414 415
  monitor->priv->started = FALSE;
  GST_OBJECT_UNLOCK (monitor);
416 417 418

}

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

444 445
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
  g_return_val_if_fail (!monitor->priv->started, 0);
446

447
  GST_OBJECT_LOCK (monitor);
448

449 450 451 452 453 454 455 456
  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);
457

458
  factories = gst_device_provider_factory_list_get_device_providers (1);
459

460 461
  while (factories) {
    GstDeviceProviderFactory *factory = factories->data;
462

463

464 465
    if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
      GstDeviceProvider *provider;
466

467
      provider = gst_device_provider_factory_get (factory);
468

469 470
      if (provider) {
        guint i;
471

472 473 474 475 476 477 478 479 480
        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;
          }
        }
      }
481

482 483
      if (provider) {
        GstBus *bus = gst_device_provider_get_bus (provider);
484

485 486 487 488 489 490 491 492
        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++;
      }
493 494
    }

495
    factories = g_list_remove (factories, factory);
496 497 498
    gst_object_unref (factory);
  }

499 500
  /* Ensure there is no leak here */
  g_assert (factories == NULL);
501

502 503 504 505 506 507
  if (matched) {
    id = filter->id;
    g_ptr_array_add (monitor->priv->filters, filter);
  } else {
    device_filter_free (filter);
  }
508

509
  GST_OBJECT_UNLOCK (monitor);
510

511
  return id;
512 513
}

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

532 533 534
  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);
535

536
  GST_OBJECT_LOCK (monitor);
537 538 539 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
  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--;
      }
    }
  }

574
  GST_OBJECT_UNLOCK (monitor);
575

576
  return removed;
577 578
}

579 580


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

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

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