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