~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to sys/winks/gstksvideosrc.c

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2009-02-23 02:23:58 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20090223022358-9yhx5izc7dz60yc8
Tags: 0.10.10-0ubuntu1
* New upstream release.
* debian/rules
  - Disable some plugins which get built by default but we do not ship in 
    multiverse package.
  - Do not build docs as there is no multiverse-doc package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Library General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
 
 
20
/**
 
21
 * SECTION:element-ksvideosrc
 
22
 *
 
23
 * Provides low-latency video capture from WDM cameras on Windows.
 
24
 *
 
25
 * <refsect2>
 
26
 * <title>Example pipelines</title>
 
27
 * |[
 
28
 * gst-launch -v ksvideosrc do-stats=TRUE ! ffmpegcolorspace ! dshowvideosink
 
29
 * ]| Capture from a camera and render using dshowvideosink.
 
30
 * |[
 
31
 * gst-launch -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
 
32
 * ! jpegdec ! ffmpegcolorspace ! dshowvideosink
 
33
 * ]| Capture from an MJPEG camera and render using dshowvideosink.
 
34
 * </refsect2>
 
35
 */
 
36
 
 
37
#ifdef HAVE_CONFIG_H
 
38
# include <config.h>
 
39
#endif
 
40
 
 
41
#include "gstksvideosrc.h"
 
42
 
 
43
#include "gstksclock.h"
 
44
#include "gstksvideodevice.h"
 
45
#include "kshelpers.h"
 
46
#include "ksvideohelpers.h"
 
47
 
 
48
#define DEFAULT_DEVICE_PATH     NULL
 
49
#define DEFAULT_DEVICE_NAME     NULL
 
50
#define DEFAULT_DEVICE_INDEX    -1
 
51
#define DEFAULT_DO_STATS        FALSE
 
52
#define DEFAULT_ENABLE_QUIRKS   TRUE
 
53
 
 
54
enum
 
55
{
 
56
  PROP_0,
 
57
  PROP_DEVICE_PATH,
 
58
  PROP_DEVICE_NAME,
 
59
  PROP_DEVICE_INDEX,
 
60
  PROP_DO_STATS,
 
61
  PROP_FPS,
 
62
  PROP_ENABLE_QUIRKS,
 
63
};
 
64
 
 
65
GST_DEBUG_CATEGORY (gst_ks_debug);
 
66
#define GST_CAT_DEFAULT gst_ks_debug
 
67
 
 
68
#define KS_WORKER_LOCK(priv) g_mutex_lock (priv->worker_lock)
 
69
#define KS_WORKER_UNLOCK(priv) g_mutex_unlock (priv->worker_lock)
 
70
#define KS_WORKER_WAIT(priv) \
 
71
    g_cond_wait (priv->worker_notify_cond, priv->worker_lock)
 
72
#define KS_WORKER_NOTIFY(priv) g_cond_signal (priv->worker_notify_cond)
 
73
#define KS_WORKER_WAIT_FOR_RESULT(priv) \
 
74
    g_cond_wait (priv->worker_result_cond, priv->worker_lock)
 
75
#define KS_WORKER_NOTIFY_RESULT(priv) \
 
76
    g_cond_signal (priv->worker_result_cond)
 
77
 
 
78
typedef enum
 
79
{
 
80
  KS_WORKER_STATE_STARTING,
 
81
  KS_WORKER_STATE_READY,
 
82
  KS_WORKER_STATE_STOPPING,
 
83
  KS_WORKER_STATE_ERROR,
 
84
} KsWorkerState;
 
85
 
 
86
typedef struct
 
87
{
 
88
  /* Properties */
 
89
  gchar *device_path;
 
90
  gchar *device_name;
 
91
  gint device_index;
 
92
  gboolean do_stats;
 
93
  gboolean enable_quirks;
 
94
 
 
95
  /* State */
 
96
  GstKsClock *ksclock;
 
97
  GstKsVideoDevice *device;
 
98
 
 
99
  guint64 offset;
 
100
  GstClockTime prev_ts;
 
101
  gboolean running;
 
102
 
 
103
  /* Worker thread */
 
104
  GThread *worker_thread;
 
105
  GMutex *worker_lock;
 
106
  GCond *worker_notify_cond;
 
107
  GCond *worker_result_cond;
 
108
  KsWorkerState worker_state;
 
109
 
 
110
  GstCaps *worker_pending_caps;
 
111
  gboolean worker_setcaps_result;
 
112
 
 
113
  gboolean worker_pending_run;
 
114
  gboolean worker_run_result;
 
115
 
 
116
  /* Statistics */
 
117
  GstClockTime last_sampling;
 
118
  guint count;
 
119
  guint fps;
 
120
} GstKsVideoSrcPrivate;
 
121
 
 
122
#define GST_KS_VIDEO_SRC_GET_PRIVATE(o) \
 
123
    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_KS_VIDEO_SRC, \
 
124
    GstKsVideoSrcPrivate))
 
125
 
 
126
static void gst_ks_video_src_finalize (GObject * object);
 
127
static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
 
128
    GValue * value, GParamSpec * pspec);
 
129
static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
 
130
    const GValue * value, GParamSpec * pspec);
 
131
 
 
132
static void gst_ks_video_src_reset (GstKsVideoSrc * self);
 
133
 
 
134
static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
 
135
    GstStateChange transition);
 
136
static gboolean gst_ks_video_src_set_clock (GstElement * element,
 
137
    GstClock * clock);
 
138
 
 
139
static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc);
 
140
static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
 
141
    GstCaps * caps);
 
142
static void gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
 
143
static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
 
144
static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
 
145
static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
 
146
 
 
147
static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
 
148
    GstBuffer ** buffer);
 
149
 
 
150
GST_BOILERPLATE (GstKsVideoSrc, gst_ks_video_src, GstPushSrc,
 
151
    GST_TYPE_PUSH_SRC);
 
152
 
 
153
static void
 
154
gst_ks_video_src_base_init (gpointer gclass)
 
155
{
 
156
  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
 
157
  static GstElementDetails element_details = {
 
158
    "KsVideoSrc",
 
159
    "Source/Video",
 
160
    "Stream data from a video capture device through Windows kernel streaming",
 
161
    "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>\n"
 
162
        "Haakon Sporsheim <hakon.sporsheim@tandberg.com>"
 
163
  };
 
164
 
 
165
  gst_element_class_set_details (element_class, &element_details);
 
166
 
 
167
  gst_element_class_add_pad_template (element_class,
 
168
      gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
 
169
          ks_video_get_all_caps ()));
 
170
}
 
171
 
 
172
static void
 
173
gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
 
174
{
 
175
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
176
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
 
177
  GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
 
178
  GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
 
179
 
 
180
  g_type_class_add_private (klass, sizeof (GstKsVideoSrcPrivate));
 
181
 
 
182
  gobject_class->finalize = gst_ks_video_src_finalize;
 
183
  gobject_class->get_property = gst_ks_video_src_get_property;
 
184
  gobject_class->set_property = gst_ks_video_src_set_property;
 
185
 
 
186
  gstelement_class->change_state =
 
187
      GST_DEBUG_FUNCPTR (gst_ks_video_src_change_state);
 
188
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_clock);
 
189
 
 
190
  gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_get_caps);
 
191
  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_caps);
 
192
  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_ks_video_src_fixate);
 
193
  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ks_video_src_query);
 
194
  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock);
 
195
  gstbasesrc_class->unlock_stop =
 
196
      GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock_stop);
 
197
 
 
198
  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_ks_video_src_create);
 
199
 
 
200
  g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
 
201
      g_param_spec_string ("device-path", "Device Path",
 
202
          "The device path", DEFAULT_DEVICE_PATH,
 
203
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
204
  g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
 
205
      g_param_spec_string ("device-name", "Device Name",
 
206
          "The human-readable device name", DEFAULT_DEVICE_NAME,
 
207
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
208
  g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
 
209
      g_param_spec_int ("device-index", "Device Index",
 
210
          "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
 
211
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
212
  g_object_class_install_property (gobject_class, PROP_DO_STATS,
 
213
      g_param_spec_boolean ("do-stats", "Enable statistics",
 
214
          "Enable logging of statistics", DEFAULT_DO_STATS,
 
215
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
216
  g_object_class_install_property (gobject_class, PROP_FPS,
 
217
      g_param_spec_int ("fps", "Frames per second",
 
218
          "Last measured framerate, if statistics are enabled",
 
219
          -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
220
  g_object_class_install_property (gobject_class, PROP_ENABLE_QUIRKS,
 
221
      g_param_spec_boolean ("enable-quirks", "Enable quirks",
 
222
          "Enable driver-specific quirks", DEFAULT_ENABLE_QUIRKS,
 
223
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
224
 
 
225
  GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
 
226
      0, "Kernel streaming video source");
 
227
}
 
228
 
 
229
static void
 
230
gst_ks_video_src_init (GstKsVideoSrc * self, GstKsVideoSrcClass * gclass)
 
231
{
 
232
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
233
  GstBaseSrc *basesrc = GST_BASE_SRC (self);
 
234
 
 
235
  gst_base_src_set_live (basesrc, TRUE);
 
236
  gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
 
237
 
 
238
  gst_ks_video_src_reset (self);
 
239
 
 
240
  priv->device_path = DEFAULT_DEVICE_PATH;
 
241
  priv->device_name = DEFAULT_DEVICE_NAME;
 
242
  priv->device_index = DEFAULT_DEVICE_INDEX;
 
243
  priv->do_stats = DEFAULT_DO_STATS;
 
244
  priv->enable_quirks = DEFAULT_ENABLE_QUIRKS;
 
245
}
 
246
 
 
247
static void
 
248
gst_ks_video_src_finalize (GObject * object)
 
249
{
 
250
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
 
251
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
252
 
 
253
  g_free (priv->device_name);
 
254
  g_free (priv->device_path);
 
255
 
 
256
  G_OBJECT_CLASS (parent_class)->finalize (object);
 
257
}
 
258
 
 
259
static void
 
260
gst_ks_video_src_get_property (GObject * object, guint prop_id,
 
261
    GValue * value, GParamSpec * pspec)
 
262
{
 
263
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
 
264
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
265
 
 
266
  switch (prop_id) {
 
267
    case PROP_DEVICE_PATH:
 
268
      g_value_set_string (value, priv->device_path);
 
269
      break;
 
270
    case PROP_DEVICE_NAME:
 
271
      g_value_set_string (value, priv->device_name);
 
272
      break;
 
273
    case PROP_DEVICE_INDEX:
 
274
      g_value_set_int (value, priv->device_index);
 
275
      break;
 
276
    case PROP_DO_STATS:
 
277
      GST_OBJECT_LOCK (object);
 
278
      g_value_set_boolean (value, priv->do_stats);
 
279
      GST_OBJECT_UNLOCK (object);
 
280
      break;
 
281
    case PROP_FPS:
 
282
      GST_OBJECT_LOCK (object);
 
283
      g_value_set_int (value, priv->fps);
 
284
      GST_OBJECT_UNLOCK (object);
 
285
      break;
 
286
    case PROP_ENABLE_QUIRKS:
 
287
      g_value_set_boolean (value, priv->enable_quirks);
 
288
      break;
 
289
    default:
 
290
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
291
      break;
 
292
  }
 
293
}
 
294
 
 
295
static void
 
296
gst_ks_video_src_set_property (GObject * object, guint prop_id,
 
297
    const GValue * value, GParamSpec * pspec)
 
298
{
 
299
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
 
300
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
301
 
 
302
  switch (prop_id) {
 
303
    case PROP_DEVICE_PATH:
 
304
      g_free (priv->device_path);
 
305
      priv->device_path = g_value_dup_string (value);
 
306
      break;
 
307
    case PROP_DEVICE_NAME:
 
308
      g_free (priv->device_name);
 
309
      priv->device_name = g_value_dup_string (value);
 
310
      break;
 
311
    case PROP_DEVICE_INDEX:
 
312
      priv->device_index = g_value_get_int (value);
 
313
      break;
 
314
    case PROP_DO_STATS:
 
315
      GST_OBJECT_LOCK (object);
 
316
      priv->do_stats = g_value_get_boolean (value);
 
317
      GST_OBJECT_UNLOCK (object);
 
318
      break;
 
319
    case PROP_ENABLE_QUIRKS:
 
320
      priv->enable_quirks = g_value_get_boolean (value);
 
321
      break;
 
322
    default:
 
323
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
324
      break;
 
325
  }
 
326
}
 
327
 
 
328
static void
 
329
gst_ks_video_src_reset (GstKsVideoSrc * self)
 
330
{
 
331
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
332
 
 
333
  /* Reset statistics */
 
334
  priv->last_sampling = GST_CLOCK_TIME_NONE;
 
335
  priv->count = 0;
 
336
  priv->fps = -1;
 
337
 
 
338
  /* Reset timestamping state */
 
339
  priv->offset = 0;
 
340
  priv->prev_ts = GST_CLOCK_TIME_NONE;
 
341
 
 
342
  priv->running = FALSE;
 
343
}
 
344
 
 
345
static void
 
346
gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
 
347
{
 
348
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
349
  HMODULE mod;
 
350
 
 
351
  /*
 
352
   * Logitech's driver software injects the following DLL into all processes
 
353
   * spawned. This DLL does some nasty tricks, sitting in between the
 
354
   * application and the low-level ntdll API (NtCreateFile, NtClose,
 
355
   * NtDeviceIoControlFile, NtDuplicateObject, etc.), making all sorts
 
356
   * of assumptions.
 
357
   *
 
358
   * The only regression that this quirk causes is that the video effects
 
359
   * feature doesn't work.
 
360
   */
 
361
  mod = GetModuleHandle ("LVPrcInj.dll");
 
362
  if (mod != NULL) {
 
363
    GST_DEBUG_OBJECT (self, "Logitech DLL detected, neutralizing it");
 
364
 
 
365
    /*
 
366
     * We know that no-one's actually keeping this handle around to decrement
 
367
     * its reference count, so we'll take care of that job. The DLL's DllMain
 
368
     * implementation takes care of rolling back changes when it gets unloaded,
 
369
     * so this seems to be the cleanest and most future-proof way that we can
 
370
     * get rid of it...
 
371
     */
 
372
    FreeLibrary (mod);
 
373
 
 
374
    /* Paranoia: verify that it's no longer there */
 
375
    mod = GetModuleHandle ("LVPrcInj.dll");
 
376
    if (mod != NULL)
 
377
      GST_WARNING_OBJECT (self, "failed to neutralize Logitech DLL");
 
378
  }
 
379
}
 
380
 
 
381
static gboolean
 
382
gst_ks_video_src_open_device (GstKsVideoSrc * self)
 
383
{
 
384
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
385
  GstKsVideoDevice *device = NULL;
 
386
  GList *devices, *cur;
 
387
 
 
388
  g_assert (priv->device == NULL);
 
389
 
 
390
  devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
 
391
  if (devices == NULL)
 
392
    goto error_no_devices;
 
393
 
 
394
  for (cur = devices; cur != NULL; cur = cur->next) {
 
395
    KsDeviceEntry *entry = cur->data;
 
396
 
 
397
    GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
 
398
        entry->index, entry->name, entry->path);
 
399
  }
 
400
 
 
401
  for (cur = devices; cur != NULL && device == NULL; cur = cur->next) {
 
402
    KsDeviceEntry *entry = cur->data;
 
403
    gboolean match;
 
404
 
 
405
    if (priv->device_path != NULL) {
 
406
      match = g_strcasecmp (entry->path, priv->device_path) == 0;
 
407
    } else if (priv->device_name != NULL) {
 
408
      match = g_strcasecmp (entry->name, priv->device_name) == 0;
 
409
    } else if (priv->device_index >= 0) {
 
410
      match = entry->index == priv->device_index;
 
411
    } else {
 
412
      match = TRUE;             /* pick the first entry */
 
413
    }
 
414
 
 
415
    if (match) {
 
416
      priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
 
417
      if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
 
418
        GstClock *clock = GST_ELEMENT_CLOCK (self);
 
419
        if (clock != NULL)
 
420
          gst_ks_clock_provide_master_clock (priv->ksclock, clock);
 
421
      } else {
 
422
        GST_WARNING_OBJECT (self, "failed to create/open KsClock");
 
423
        g_object_unref (priv->ksclock);
 
424
        priv->ksclock = NULL;
 
425
      }
 
426
 
 
427
      device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
 
428
          "clock", priv->ksclock, "device-path", entry->path, NULL);
 
429
    }
 
430
 
 
431
    ks_device_entry_free (entry);
 
432
  }
 
433
 
 
434
  g_list_free (devices);
 
435
 
 
436
  if (device == NULL)
 
437
    goto error_no_match;
 
438
 
 
439
  if (!gst_ks_video_device_open (device))
 
440
    goto error_open;
 
441
 
 
442
  priv->device = device;
 
443
 
 
444
  return TRUE;
 
445
 
 
446
  /* ERRORS */
 
447
error_no_devices:
 
448
  {
 
449
    GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
 
450
        ("No video capture devices found"), (NULL));
 
451
    return FALSE;
 
452
  }
 
453
error_no_match:
 
454
  {
 
455
    if (priv->device_path != NULL) {
 
456
      GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
 
457
          ("Specified video capture device with path '%s' not found",
 
458
              priv->device_path), (NULL));
 
459
    } else if (priv->device_name != NULL) {
 
460
      GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
 
461
          ("Specified video capture device with name '%s' not found",
 
462
              priv->device_name), (NULL));
 
463
    } else {
 
464
      GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
 
465
          ("Specified video capture device with index %d not found",
 
466
              priv->device_index), (NULL));
 
467
    }
 
468
    return FALSE;
 
469
  }
 
470
error_open:
 
471
  {
 
472
    GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
 
473
        ("Failed to open device"), (NULL));
 
474
    g_object_unref (device);
 
475
    return FALSE;
 
476
  }
 
477
}
 
478
 
 
479
static void
 
480
gst_ks_video_src_close_device (GstKsVideoSrc * self)
 
481
{
 
482
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
483
 
 
484
  g_assert (priv->device != NULL);
 
485
 
 
486
  gst_ks_video_device_close (priv->device);
 
487
  g_object_unref (priv->device);
 
488
  priv->device = NULL;
 
489
 
 
490
  if (priv->ksclock != NULL) {
 
491
    gst_ks_clock_close (priv->ksclock);
 
492
    g_object_unref (priv->ksclock);
 
493
    priv->ksclock = NULL;
 
494
  }
 
495
 
 
496
  gst_ks_video_src_reset (self);
 
497
}
 
498
 
 
499
/*
 
500
 * Worker thread that takes care of starting, configuring and stopping things.
 
501
 *
 
502
 * This is needed because Logitech's driver software injects a DLL that
 
503
 * intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
 
504
 * and NtDuplicateObject so that they can provide in-place video effects to
 
505
 * existing applications. Their assumption is that at least one thread tainted
 
506
 * by their code stays around for the lifetime of the capture.
 
507
 */
 
508
static gpointer
 
509
gst_ks_video_src_worker_func (gpointer data)
 
510
{
 
511
  GstKsVideoSrc *self = data;
 
512
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
513
 
 
514
  if (!gst_ks_video_src_open_device (self))
 
515
    goto open_failed;
 
516
 
 
517
  KS_WORKER_LOCK (priv);
 
518
  priv->worker_state = KS_WORKER_STATE_READY;
 
519
  KS_WORKER_NOTIFY_RESULT (priv);
 
520
 
 
521
  while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
 
522
    KS_WORKER_WAIT (priv);
 
523
 
 
524
    if (priv->worker_pending_caps != NULL) {
 
525
      priv->worker_setcaps_result =
 
526
          gst_ks_video_device_set_caps (priv->device,
 
527
          priv->worker_pending_caps);
 
528
 
 
529
      priv->worker_pending_caps = NULL;
 
530
      KS_WORKER_NOTIFY_RESULT (priv);
 
531
    } else if (priv->worker_pending_run) {
 
532
      if (priv->ksclock != NULL)
 
533
        gst_ks_clock_start (priv->ksclock);
 
534
      priv->worker_run_result =
 
535
          gst_ks_video_device_set_state (priv->device, KSSTATE_RUN);
 
536
 
 
537
      priv->worker_pending_run = FALSE;
 
538
      KS_WORKER_NOTIFY_RESULT (priv);
 
539
    }
 
540
  }
 
541
 
 
542
  KS_WORKER_UNLOCK (priv);
 
543
 
 
544
  gst_ks_video_src_close_device (self);
 
545
 
 
546
  return NULL;
 
547
 
 
548
  /* ERRORS */
 
549
open_failed:
 
550
  {
 
551
    KS_WORKER_LOCK (priv);
 
552
    priv->worker_state = KS_WORKER_STATE_ERROR;
 
553
    KS_WORKER_NOTIFY_RESULT (priv);
 
554
    KS_WORKER_UNLOCK (priv);
 
555
 
 
556
    return NULL;
 
557
  }
 
558
}
 
559
 
 
560
static gboolean
 
561
gst_ks_video_src_start_worker (GstKsVideoSrc * self)
 
562
{
 
563
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
564
  gboolean result;
 
565
 
 
566
  priv->worker_lock = g_mutex_new ();
 
567
  priv->worker_notify_cond = g_cond_new ();
 
568
  priv->worker_result_cond = g_cond_new ();
 
569
 
 
570
  priv->worker_pending_caps = NULL;
 
571
  priv->worker_pending_run = FALSE;
 
572
 
 
573
  priv->worker_state = KS_WORKER_STATE_STARTING;
 
574
  priv->worker_thread =
 
575
      g_thread_create (gst_ks_video_src_worker_func, self, TRUE, NULL);
 
576
 
 
577
  KS_WORKER_LOCK (priv);
 
578
  while (priv->worker_state < KS_WORKER_STATE_READY)
 
579
    KS_WORKER_WAIT_FOR_RESULT (priv);
 
580
  result = priv->worker_state == KS_WORKER_STATE_READY;
 
581
  KS_WORKER_UNLOCK (priv);
 
582
 
 
583
  return result;
 
584
}
 
585
 
 
586
static void
 
587
gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
 
588
{
 
589
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
590
 
 
591
  KS_WORKER_LOCK (priv);
 
592
  priv->worker_state = KS_WORKER_STATE_STOPPING;
 
593
  KS_WORKER_NOTIFY (priv);
 
594
  KS_WORKER_UNLOCK (priv);
 
595
 
 
596
  g_thread_join (priv->worker_thread);
 
597
  priv->worker_thread = NULL;
 
598
 
 
599
  g_cond_free (priv->worker_result_cond);
 
600
  priv->worker_result_cond = NULL;
 
601
  g_cond_free (priv->worker_notify_cond);
 
602
  priv->worker_notify_cond = NULL;
 
603
  g_mutex_free (priv->worker_lock);
 
604
  priv->worker_lock = NULL;
 
605
}
 
606
 
 
607
static GstStateChangeReturn
 
608
gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
 
609
{
 
610
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
 
611
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
612
  GstStateChangeReturn ret;
 
613
 
 
614
  switch (transition) {
 
615
    case GST_STATE_CHANGE_NULL_TO_READY:
 
616
      if (priv->enable_quirks)
 
617
        gst_ks_video_src_apply_driver_quirks (self);
 
618
      if (!gst_ks_video_src_start_worker (self))
 
619
        goto open_failed;
 
620
      break;
 
621
  }
 
622
 
 
623
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 
624
 
 
625
  switch (transition) {
 
626
    case GST_STATE_CHANGE_READY_TO_NULL:
 
627
      gst_ks_video_src_stop_worker (self);
 
628
      break;
 
629
  }
 
630
 
 
631
  return ret;
 
632
 
 
633
  /* ERRORS */
 
634
open_failed:
 
635
  {
 
636
    gst_ks_video_src_stop_worker (self);
 
637
    return GST_STATE_CHANGE_FAILURE;
 
638
  }
 
639
}
 
640
 
 
641
static gboolean
 
642
gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
 
643
{
 
644
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
 
645
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
646
 
 
647
  GST_OBJECT_LOCK (element);
 
648
  if (clock != NULL && priv->ksclock != NULL)
 
649
    gst_ks_clock_provide_master_clock (priv->ksclock, clock);
 
650
  GST_OBJECT_UNLOCK (element);
 
651
 
 
652
  return TRUE;
 
653
}
 
654
 
 
655
static GstCaps *
 
656
gst_ks_video_src_get_caps (GstBaseSrc * basesrc)
 
657
{
 
658
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
 
659
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
660
 
 
661
  if (priv->device != NULL)
 
662
    return gst_ks_video_device_get_available_caps (priv->device);
 
663
  else
 
664
    return NULL;                /* BaseSrc will return template caps */
 
665
}
 
666
 
 
667
static gboolean
 
668
gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
 
669
{
 
670
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
 
671
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
672
 
 
673
  if (priv->device == NULL)
 
674
    return FALSE;
 
675
 
 
676
  KS_WORKER_LOCK (priv);
 
677
  priv->worker_pending_caps = caps;
 
678
  KS_WORKER_NOTIFY (priv);
 
679
  while (priv->worker_pending_caps == caps)
 
680
    KS_WORKER_WAIT_FOR_RESULT (priv);
 
681
  KS_WORKER_UNLOCK (priv);
 
682
 
 
683
  return priv->worker_setcaps_result;
 
684
}
 
685
 
 
686
static void
 
687
gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
 
688
{
 
689
  GstStructure *structure = gst_caps_get_structure (caps, 0);
 
690
 
 
691
  gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
 
692
  gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
 
693
  gst_structure_fixate_field_nearest_fraction (structure, "framerate",
 
694
      G_MAXINT, 1);
 
695
}
 
696
 
 
697
static gboolean
 
698
gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
 
699
{
 
700
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
 
701
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
702
  gboolean result = FALSE;
 
703
 
 
704
  switch (GST_QUERY_TYPE (query)) {
 
705
    case GST_QUERY_LATENCY:{
 
706
      GstClockTime min_latency, max_latency;
 
707
 
 
708
      if (priv->device == NULL)
 
709
        goto beach;
 
710
 
 
711
      result = gst_ks_video_device_get_latency (priv->device, &min_latency,
 
712
          &max_latency);
 
713
      if (!result)
 
714
        goto beach;
 
715
 
 
716
      GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
 
717
          " max %" GST_TIME_FORMAT,
 
718
          GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
 
719
 
 
720
      gst_query_set_latency (query, TRUE, min_latency, max_latency);
 
721
      break;
 
722
    }
 
723
    default:
 
724
      result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
 
725
      break;
 
726
  }
 
727
 
 
728
beach:
 
729
  return result;
 
730
}
 
731
 
 
732
static gboolean
 
733
gst_ks_video_src_unlock (GstBaseSrc * basesrc)
 
734
{
 
735
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
 
736
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
737
 
 
738
  GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
 
739
 
 
740
  gst_ks_video_device_cancel (priv->device);
 
741
  return TRUE;
 
742
}
 
743
 
 
744
static gboolean
 
745
gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
 
746
{
 
747
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
 
748
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
749
 
 
750
  GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
 
751
 
 
752
  gst_ks_video_device_cancel_stop (priv->device);
 
753
  return TRUE;
 
754
}
 
755
 
 
756
static gboolean
 
757
gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
 
758
    GstClockTime presentation_time)
 
759
{
 
760
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
761
  GstClockTime duration;
 
762
  GstClock *clock;
 
763
  GstClockTime timestamp;
 
764
 
 
765
  duration = gst_ks_video_device_get_duration (priv->device);
 
766
 
 
767
  GST_OBJECT_LOCK (self);
 
768
  clock = GST_ELEMENT_CLOCK (self);
 
769
  if (clock != NULL) {
 
770
    gst_object_ref (clock);
 
771
    timestamp = GST_ELEMENT (self)->base_time;
 
772
 
 
773
    if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
 
774
      if (presentation_time > GST_ELEMENT (self)->base_time)
 
775
        presentation_time -= GST_ELEMENT (self)->base_time;
 
776
      else
 
777
        presentation_time = 0;
 
778
    }
 
779
  } else {
 
780
    timestamp = GST_CLOCK_TIME_NONE;
 
781
  }
 
782
  GST_OBJECT_UNLOCK (self);
 
783
 
 
784
  if (clock != NULL) {
 
785
 
 
786
    /* The time according to the current clock */
 
787
    timestamp = gst_clock_get_time (clock) - timestamp;
 
788
    if (timestamp > duration)
 
789
      timestamp -= duration;
 
790
    else
 
791
      timestamp = 0;
 
792
 
 
793
    if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
 
794
      /*
 
795
       * We don't use this for anything yet, need to ponder how to deal
 
796
       * with pins that use an internal clock and timestamp from 0.
 
797
       */
 
798
      GstClockTimeDiff diff = GST_CLOCK_DIFF (presentation_time, timestamp);
 
799
      GST_DEBUG_OBJECT (self, "diff between gst and driver timestamp: %"
 
800
          G_GINT64_FORMAT, diff);
 
801
    }
 
802
 
 
803
    gst_object_unref (clock);
 
804
    clock = NULL;
 
805
 
 
806
    /* Unless it's the first frame, align the current timestamp on a multiple
 
807
     * of duration since the previous */
 
808
    if (GST_CLOCK_TIME_IS_VALID (priv->prev_ts)) {
 
809
      GstClockTime delta;
 
810
      guint delta_remainder, delta_offset;
 
811
 
 
812
      /* REVISIT: I've seen this happen with the GstSystemClock on Windows,
 
813
       *          scary... */
 
814
      if (timestamp < priv->prev_ts) {
 
815
        GST_WARNING_OBJECT (self, "clock is ticking backwards");
 
816
        return FALSE;
 
817
      }
 
818
 
 
819
      /* Round to a duration boundary */
 
820
      delta = timestamp - priv->prev_ts;
 
821
      delta_remainder = delta % duration;
 
822
 
 
823
      if (delta_remainder < duration / 3)
 
824
        timestamp -= delta_remainder;
 
825
      else
 
826
        timestamp += duration - delta_remainder;
 
827
 
 
828
      /* How many frames are we off then? */
 
829
      delta = timestamp - priv->prev_ts;
 
830
      delta_offset = delta / duration;
 
831
 
 
832
      if (delta_offset == 1)    /* perfect */
 
833
        GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
 
834
      else if (delta_offset > 1) {
 
835
        guint lost = delta_offset - 1;
 
836
        GST_INFO_OBJECT (self, "lost %d frame%s, setting discont flag",
 
837
            lost, (lost > 1) ? "s" : "");
 
838
        GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
 
839
      } else if (delta_offset == 0) {   /* overproduction, skip this frame */
 
840
        GST_INFO_OBJECT (self, "skipping frame");
 
841
        return FALSE;
 
842
      }
 
843
 
 
844
      priv->offset += delta_offset;
 
845
    }
 
846
 
 
847
    priv->prev_ts = timestamp;
 
848
  }
 
849
 
 
850
  GST_BUFFER_OFFSET (buf) = priv->offset;
 
851
  GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
 
852
  GST_BUFFER_TIMESTAMP (buf) = timestamp;
 
853
  GST_BUFFER_DURATION (buf) = duration;
 
854
 
 
855
  return TRUE;
 
856
}
 
857
 
 
858
static void
 
859
gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
 
860
{
 
861
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
862
  GstClock *clock;
 
863
 
 
864
  GST_OBJECT_LOCK (self);
 
865
  clock = GST_ELEMENT_CLOCK (self);
 
866
  if (clock != NULL)
 
867
    gst_object_ref (clock);
 
868
  GST_OBJECT_UNLOCK (self);
 
869
 
 
870
  if (clock != NULL) {
 
871
    GstClockTime now = gst_clock_get_time (clock);
 
872
    gst_object_unref (clock);
 
873
 
 
874
    priv->count++;
 
875
 
 
876
    if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
 
877
      if (now - priv->last_sampling >= GST_SECOND) {
 
878
        GST_OBJECT_LOCK (self);
 
879
        priv->fps = priv->count;
 
880
        GST_OBJECT_UNLOCK (self);
 
881
 
 
882
        g_object_notify (G_OBJECT (self), "fps");
 
883
 
 
884
        priv->last_sampling = now;
 
885
        priv->count = 0;
 
886
      }
 
887
    } else {
 
888
      priv->last_sampling = now;
 
889
    }
 
890
  }
 
891
}
 
892
 
 
893
static GstFlowReturn
 
894
gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
 
895
{
 
896
  GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
 
897
  GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
 
898
  guint buf_size;
 
899
  GstCaps *caps;
 
900
  GstBuffer *buf = NULL;
 
901
  GstFlowReturn result;
 
902
  GstClockTime presentation_time;
 
903
  gulong error_code;
 
904
  gchar *error_str;
 
905
 
 
906
  g_assert (priv->device != NULL);
 
907
 
 
908
  if (!gst_ks_video_device_has_caps (priv->device))
 
909
    goto error_no_caps;
 
910
 
 
911
  buf_size = gst_ks_video_device_get_frame_size (priv->device);
 
912
  g_assert (buf_size);
 
913
 
 
914
  caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self));
 
915
  if (caps == NULL)
 
916
    goto error_no_caps;
 
917
  result = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), priv->offset,
 
918
      buf_size, caps, &buf);
 
919
  gst_caps_unref (caps);
 
920
  if (G_UNLIKELY (result != GST_FLOW_OK))
 
921
    goto error_alloc_buffer;
 
922
 
 
923
  if (G_UNLIKELY (!priv->running)) {
 
924
    KS_WORKER_LOCK (priv);
 
925
    priv->worker_pending_run = TRUE;
 
926
    KS_WORKER_NOTIFY (priv);
 
927
    while (priv->worker_pending_run)
 
928
      KS_WORKER_WAIT_FOR_RESULT (priv);
 
929
    priv->running = priv->worker_run_result;
 
930
    KS_WORKER_UNLOCK (priv);
 
931
 
 
932
    if (!priv->running)
 
933
      goto error_start_capture;
 
934
  }
 
935
 
 
936
  do {
 
937
    gulong bytes_read;
 
938
 
 
939
    result = gst_ks_video_device_read_frame (priv->device,
 
940
        GST_BUFFER_DATA (buf), buf_size, &bytes_read, &presentation_time,
 
941
        &error_code, &error_str);
 
942
    if (G_UNLIKELY (result != GST_FLOW_OK))
 
943
      goto error_read_frame;
 
944
 
 
945
    GST_BUFFER_SIZE (buf) = bytes_read;
 
946
  }
 
947
  while (!gst_ks_video_src_timestamp_buffer (self, buf, presentation_time));
 
948
 
 
949
  if (G_UNLIKELY (priv->do_stats))
 
950
    gst_ks_video_src_update_statistics (self);
 
951
 
 
952
  gst_ks_video_device_postprocess_frame (priv->device,
 
953
      GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
 
954
 
 
955
  *buffer = buf;
 
956
  return GST_FLOW_OK;
 
957
 
 
958
  /* ERRORS */
 
959
error_no_caps:
 
960
  {
 
961
    GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
 
962
        ("not negotiated"), ("maybe setcaps failed?"));
 
963
 
 
964
    return GST_FLOW_ERROR;
 
965
  }
 
966
error_start_capture:
 
967
  {
 
968
    GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
 
969
        ("could not start capture"),
 
970
        ("failed to change pin state to KSSTATE_RUN"));
 
971
 
 
972
    return GST_FLOW_ERROR;
 
973
  }
 
974
error_alloc_buffer:
 
975
  {
 
976
    GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
 
977
 
 
978
    return result;
 
979
  }
 
980
error_read_frame:
 
981
  {
 
982
    if (result != GST_FLOW_WRONG_STATE && result != GST_FLOW_UNEXPECTED) {
 
983
      GST_ELEMENT_ERROR (self, RESOURCE, READ,
 
984
          ("read failed: %s [0x%08x]", error_str, error_code),
 
985
          ("gst_ks_video_device_read_frame failed"));
 
986
    }
 
987
 
 
988
    g_free (error_str);
 
989
    gst_buffer_unref (buf);
 
990
 
 
991
    return result;
 
992
  }
 
993
}
 
994
 
 
995
static gboolean
 
996
plugin_init (GstPlugin * plugin)
 
997
{
 
998
  return gst_element_register (plugin, "ksvideosrc",
 
999
      GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC);
 
1000
}
 
1001
 
 
1002
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
 
1003
    GST_VERSION_MINOR,
 
1004
    "winks",
 
1005
    "Windows kernel streaming plugin",
 
1006
    plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")