gstdevicemonitor.c 15.2 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 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
 *
 * The basic use pattern of an iterator is as follows:
 * |[
 *   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 351 352 353 354
  if (monitor->priv->filters->len == 0) {
    GST_OBJECT_UNLOCK (monitor);
    GST_WARNING_OBJECT (monitor, "No filters have been set");
    return FALSE;
  }

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

361
  gst_bus_set_flushing (monitor->priv->bus, FALSE);
362

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

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

375 376 377
        GST_OBJECT_UNLOCK (monitor);
        return FALSE;
      }
378 379 380
    }
  }

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

  return TRUE;
}

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

400
  g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
401

402
  gst_bus_set_flushing (monitor->priv->bus, TRUE);
403

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

}

417
/**
418 419 420 421
 * 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
422
 *
423 424 425 426 427 428 429
 * 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.
430 431 432
 *
 * Since: 1.4
 */
433 434 435
guint
gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
    const gchar * classes, GstCaps * caps)
436 437
{
  GList *factories = NULL;
438 439 440
  struct DeviceFilter *filter;
  guint id = 0;
  gboolean matched = FALSE;
441

442 443
  g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
  g_return_val_if_fail (!monitor->priv->started, 0);
444

445
  GST_OBJECT_LOCK (monitor);
446

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

456
  factories = gst_device_provider_factory_list_get_device_providers (1);
457

458 459
  while (factories) {
    GstDeviceProviderFactory *factory = factories->data;
460

461

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

465
      provider = gst_device_provider_factory_get (factory);
466

467 468
      if (provider) {
        guint i;
469

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

480 481
      if (provider) {
        GstBus *bus = gst_device_provider_get_bus (provider);
482

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

493
    factories = g_list_remove (factories, factory);
494 495 496
    gst_object_unref (factory);
  }

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

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

507
  GST_OBJECT_UNLOCK (monitor);
508

509
  return id;
510 511
}

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

530 531 532
  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);
533

534
  GST_OBJECT_LOCK (monitor);
535 536 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
  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--;
      }
    }
  }

572
  GST_OBJECT_UNLOCK (monitor);
573

574
  return removed;
575 576
}

577 578


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

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

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