taglist, plugins: fix compiler warnings with GLib >= 2.76
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / 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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-ksvideosrc
23  * @title: ksvideosrc
24  *
25  * Provides low-latency video capture from WDM cameras on Windows.
26  *
27  * ## Example pipelines
28  * |[
29  * gst-launch-1.0 -v ksvideosrc do-stats=TRUE ! videoconvert ! dshowvideosink
30  * ]| Capture from a camera and render using dshowvideosink.
31  * |[
32  * gst-launch-1.0 -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
33  * ! jpegdec ! videoconvert ! dshowvideosink
34  * ]| Capture from an MJPEG camera and render using dshowvideosink.
35  *
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 #include "ksdeviceprovider.h"
49
50 #include <versionhelpers.h>
51
52 #define DEFAULT_DEVICE_PATH     NULL
53 #define DEFAULT_DEVICE_NAME     NULL
54 #define DEFAULT_DEVICE_INDEX    -1
55 #define DEFAULT_DO_STATS        FALSE
56 #define DEFAULT_ENABLE_QUIRKS   TRUE
57
58 enum
59 {
60   PROP_0,
61   PROP_DEVICE_PATH,
62   PROP_DEVICE_NAME,
63   PROP_DEVICE_INDEX,
64   PROP_DO_STATS,
65   PROP_FPS,
66   PROP_ENABLE_QUIRKS,
67 };
68
69 GST_DEBUG_CATEGORY (gst_ks_debug);
70 #define GST_CAT_DEFAULT gst_ks_debug
71
72 #define KS_WORKER_LOCK(priv) g_mutex_lock (&priv->worker_lock)
73 #define KS_WORKER_UNLOCK(priv) g_mutex_unlock (&priv->worker_lock)
74 #define KS_WORKER_WAIT(priv) \
75     g_cond_wait (&priv->worker_notify_cond, &priv->worker_lock)
76 #define KS_WORKER_NOTIFY(priv) g_cond_signal (&priv->worker_notify_cond)
77 #define KS_WORKER_WAIT_FOR_RESULT(priv) \
78     g_cond_wait (&priv->worker_result_cond, &priv->worker_lock)
79 #define KS_WORKER_NOTIFY_RESULT(priv) \
80     g_cond_signal (&priv->worker_result_cond)
81
82 typedef enum
83 {
84   KS_WORKER_STATE_STARTING,
85   KS_WORKER_STATE_READY,
86   KS_WORKER_STATE_STOPPING,
87   KS_WORKER_STATE_ERROR,
88 } KsWorkerState;
89
90 struct _GstKsVideoSrcPrivate
91 {
92   /* Properties */
93   gchar *device_path;
94   gchar *device_name;
95   gint device_index;
96   gboolean do_stats;
97   gboolean enable_quirks;
98
99   /* State */
100   GstKsClock *ksclock;
101   GstKsVideoDevice *device;
102
103   gboolean running;
104
105   /* Worker thread */
106   GThread *worker_thread;
107   GMutex worker_lock;
108   GCond worker_notify_cond;
109   GCond worker_result_cond;
110   KsWorkerState worker_state;
111
112   GstCaps *worker_pending_caps;
113   gboolean worker_setcaps_result;
114
115   gboolean worker_pending_run;
116   gboolean worker_run_result;
117
118   gulong worker_error_code;
119
120   /* Statistics */
121   GstClockTime last_sampling;
122   guint count;
123   guint fps;
124 };
125
126 #define GST_KS_VIDEO_SRC_GET_PRIVATE(o) ((o)->priv)
127
128 static void gst_ks_video_src_finalize (GObject * object);
129 static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
130     GValue * value, GParamSpec * pspec);
131 static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
132     const GValue * value, GParamSpec * pspec);
133
134 G_GNUC_UNUSED static GArray
135     * gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self);
136 static void gst_ks_video_src_reset (GstKsVideoSrc * self);
137
138 static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
139     GstStateChange transition);
140 static gboolean gst_ks_video_src_set_clock (GstElement * element,
141     GstClock * clock);
142
143 static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc,
144     GstCaps * filter);
145 static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
146     GstCaps * caps);
147 static GstCaps *gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
148 static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
149 static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
150 static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
151
152 static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
153     GstBuffer ** buffer);
154 static GstBuffer *gst_ks_video_src_alloc_buffer (guint size, guint alignment,
155     gpointer user_data);
156
157 G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoSrc, gst_ks_video_src, GST_TYPE_PUSH_SRC);
158
159 static GstKsVideoSrcClass *parent_class = NULL;
160
161 static void
162 gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
163 {
164   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
166   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
167   GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
168
169   parent_class = g_type_class_peek_parent (klass);
170
171   gst_element_class_set_static_metadata (gstelement_class, "KsVideoSrc",
172       "Source/Video/Hardware",
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 (gstelement_class,
179       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
180           ks_video_get_all_caps ()));
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
226 static void
227 gst_ks_video_src_init (GstKsVideoSrc * self)
228 {
229   GstBaseSrc *basesrc = GST_BASE_SRC (self);
230   GstKsVideoSrcPrivate *priv;
231
232   self->priv = gst_ks_video_src_get_instance_private (self);
233   priv = GST_KS_VIDEO_SRC_GET_PRIVATE (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   /* MediaFoundation does not support MinGW build */
247 #ifdef _MSC_VER
248   {
249     static gsize deprecated_warn = 0;
250
251     if (g_once_init_enter (&deprecated_warn)) {
252       if (IsWindows8OrGreater ()) {
253         g_warning ("\"ksvideosrc\" is deprecated and will be removed"
254             "in the future. Use \"mfvideosrc\" element instead");
255       }
256       g_once_init_leave (&deprecated_warn, 1);
257     }
258   }
259 #endif
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     {
320       const gchar *device_path = g_value_get_string (value);
321       g_free (priv->device_path);
322       priv->device_path = NULL;
323       if (device_path && strlen (device_path) != 0)
324         priv->device_path = g_value_dup_string (value);
325     }
326       break;
327     case PROP_DEVICE_NAME:
328     {
329       const gchar *device_name = g_value_get_string (value);
330       g_free (priv->device_name);
331       priv->device_name = NULL;
332       if (device_name && strlen (device_name) != 0)
333         priv->device_name = g_strdup (device_name);
334     }
335       break;
336     case PROP_DEVICE_INDEX:
337       priv->device_index = g_value_get_int (value);
338       break;
339     case PROP_DO_STATS:
340       GST_OBJECT_LOCK (object);
341       priv->do_stats = g_value_get_boolean (value);
342       GST_OBJECT_UNLOCK (object);
343       break;
344     case PROP_ENABLE_QUIRKS:
345       priv->enable_quirks = g_value_get_boolean (value);
346       break;
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349       break;
350   }
351 }
352
353 static void
354 gst_ks_video_src_reset (GstKsVideoSrc * self)
355 {
356   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
357
358   /* Reset statistics */
359   priv->last_sampling = GST_CLOCK_TIME_NONE;
360   priv->count = 0;
361   priv->fps = -1;
362
363   priv->running = FALSE;
364 }
365
366 static void
367 gst_ks_video_src_apply_driver_quirks (GstKsVideoSrc * self)
368 {
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 /*FIXME: when we have a devices API replacement */
402 G_GNUC_UNUSED static GArray *
403 gst_ks_video_src_get_device_name_values (GstKsVideoSrc * self)
404 {
405   GList *devices, *cur;
406   GArray *array = g_array_new (TRUE, TRUE, sizeof (GValue));
407
408   devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
409   if (devices == NULL)
410     return array;
411
412   devices = ks_video_device_list_sort_cameras_first (devices);
413
414   for (cur = devices; cur != NULL; cur = cur->next) {
415     GValue value = { 0, };
416     KsDeviceEntry *entry = cur->data;
417
418     g_value_init (&value, G_TYPE_STRING);
419     g_value_set_string (&value, entry->name);
420     g_array_append_val (array, value);
421     g_value_unset (&value);
422
423     ks_device_entry_free (entry);
424   }
425
426   g_list_free (devices);
427   return array;
428 }
429
430 static gboolean
431 gst_ks_video_src_open_device (GstKsVideoSrc * self)
432 {
433   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
434   GstKsVideoDevice *device = NULL;
435   GList *devices, *cur;
436
437   g_assert (priv->device == NULL);
438
439   devices = ks_enumerate_devices (&KSCATEGORY_VIDEO, &KSCATEGORY_CAPTURE);
440   if (devices == NULL)
441     goto error_no_devices;
442
443   devices = ks_video_device_list_sort_cameras_first (devices);
444
445   for (cur = devices; cur != NULL; cur = cur->next) {
446     KsDeviceEntry *entry = cur->data;
447
448     GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
449         entry->index, entry->name, entry->path);
450   }
451
452   for (cur = devices; cur != NULL; cur = cur->next) {
453     KsDeviceEntry *entry = cur->data;
454     gboolean match;
455
456     if (device != NULL) {
457       ks_device_entry_free (entry);
458       continue;
459     }
460     if (priv->device_path != NULL) {
461       match = g_ascii_strcasecmp (entry->path, priv->device_path) == 0;
462     } else if (priv->device_name != NULL) {
463       match = g_ascii_strcasecmp (entry->name, priv->device_name) == 0;
464     } else if (priv->device_index >= 0) {
465       match = entry->index == priv->device_index;
466     } else {
467       match = TRUE;             /* pick the first entry */
468     }
469
470     if (match) {
471       priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
472       if (priv->ksclock != NULL && gst_ks_clock_open (priv->ksclock)) {
473         GstClock *clock = GST_ELEMENT_CLOCK (self);
474         if (clock != NULL)
475           gst_ks_clock_provide_master_clock (priv->ksclock, clock);
476       } else {
477         GST_WARNING_OBJECT (self, "failed to create/open KsClock");
478         g_object_unref (priv->ksclock);
479         priv->ksclock = NULL;
480       }
481
482       device = gst_ks_video_device_new (entry->path, priv->ksclock,
483           gst_ks_video_src_alloc_buffer, self);
484     }
485
486     ks_device_entry_free (entry);
487   }
488
489   g_list_free (devices);
490
491   if (device == NULL)
492     goto error_no_match;
493
494   if (!gst_ks_video_device_open (device))
495     goto error_open;
496
497   priv->device = device;
498
499   return TRUE;
500
501   /* ERRORS */
502 error_no_devices:
503   {
504     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
505         ("No video capture devices found"), (NULL));
506     return FALSE;
507   }
508 error_no_match:
509   {
510     if (priv->device_path != NULL) {
511       GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
512           ("Specified video capture device with path '%s' not found",
513               priv->device_path), (NULL));
514     } else if (priv->device_name != NULL) {
515       GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
516           ("Specified video capture device with name '%s' not found",
517               priv->device_name), (NULL));
518     } else {
519       GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
520           ("Specified video capture device with index %d not found",
521               priv->device_index), (NULL));
522     }
523     return FALSE;
524   }
525 error_open:
526   {
527     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
528         ("Failed to open device"), (NULL));
529     g_object_unref (device);
530     return FALSE;
531   }
532 }
533
534 static void
535 gst_ks_video_src_close_device (GstKsVideoSrc * self)
536 {
537   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
538
539   g_assert (priv->device != NULL);
540
541   gst_ks_video_device_close (priv->device);
542   g_object_unref (priv->device);
543   priv->device = NULL;
544
545   if (priv->ksclock != NULL) {
546     gst_ks_clock_close (priv->ksclock);
547     g_object_unref (priv->ksclock);
548     priv->ksclock = NULL;
549   }
550
551   gst_ks_video_src_reset (self);
552 }
553
554 /*
555  * Worker thread that takes care of starting, configuring and stopping things.
556  *
557  * This is needed because Logitech's driver software injects a DLL that
558  * intercepts API functions like NtCreateFile, NtClose, NtDeviceIoControlFile
559  * and NtDuplicateObject so that they can provide in-place video effects to
560  * existing applications. Their assumption is that at least one thread tainted
561  * by their code stays around for the lifetime of the capture.
562  */
563 static gpointer
564 gst_ks_video_src_worker_func (gpointer data)
565 {
566   GstKsVideoSrc *self = data;
567   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
568
569   if (!gst_ks_video_src_open_device (self))
570     goto open_failed;
571
572   KS_WORKER_LOCK (priv);
573   priv->worker_state = KS_WORKER_STATE_READY;
574   KS_WORKER_NOTIFY_RESULT (priv);
575
576   while (priv->worker_state != KS_WORKER_STATE_STOPPING) {
577     KS_WORKER_WAIT (priv);
578
579     if (priv->worker_pending_caps != NULL) {
580       priv->worker_setcaps_result =
581           gst_ks_video_device_set_caps (priv->device,
582           priv->worker_pending_caps);
583
584       priv->worker_pending_caps = NULL;
585       KS_WORKER_NOTIFY_RESULT (priv);
586     } else if (priv->worker_pending_run) {
587       if (priv->ksclock != NULL)
588         gst_ks_clock_start (priv->ksclock);
589       priv->worker_run_result = gst_ks_video_device_set_state (priv->device,
590           KSSTATE_RUN, &priv->worker_error_code);
591
592       priv->worker_pending_run = FALSE;
593       KS_WORKER_NOTIFY_RESULT (priv);
594     }
595   }
596
597   KS_WORKER_UNLOCK (priv);
598
599   gst_ks_video_src_close_device (self);
600
601   return NULL;
602
603   /* ERRORS */
604 open_failed:
605   {
606     KS_WORKER_LOCK (priv);
607     priv->worker_state = KS_WORKER_STATE_ERROR;
608     KS_WORKER_NOTIFY_RESULT (priv);
609     KS_WORKER_UNLOCK (priv);
610
611     return NULL;
612   }
613 }
614
615 static gboolean
616 gst_ks_video_src_start_worker (GstKsVideoSrc * self)
617 {
618   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
619   gboolean result;
620
621   g_mutex_init (&priv->worker_lock);
622   g_cond_init (&priv->worker_notify_cond);
623   g_cond_init (&priv->worker_result_cond);
624
625   priv->worker_pending_caps = NULL;
626   priv->worker_pending_run = FALSE;
627
628   priv->worker_state = KS_WORKER_STATE_STARTING;
629   priv->worker_thread =
630       g_thread_new ("ks-worker", gst_ks_video_src_worker_func, self);
631
632   KS_WORKER_LOCK (priv);
633   while (priv->worker_state < KS_WORKER_STATE_READY)
634     KS_WORKER_WAIT_FOR_RESULT (priv);
635   result = priv->worker_state == KS_WORKER_STATE_READY;
636   KS_WORKER_UNLOCK (priv);
637
638   return result;
639 }
640
641 static void
642 gst_ks_video_src_stop_worker (GstKsVideoSrc * self)
643 {
644   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
645
646   KS_WORKER_LOCK (priv);
647   priv->worker_state = KS_WORKER_STATE_STOPPING;
648   KS_WORKER_NOTIFY (priv);
649   KS_WORKER_UNLOCK (priv);
650
651   g_thread_join (priv->worker_thread);
652   priv->worker_thread = NULL;
653
654   g_cond_clear (&priv->worker_result_cond);
655   g_cond_clear (&priv->worker_notify_cond);
656   g_mutex_clear (&priv->worker_lock);
657 }
658
659 static GstStateChangeReturn
660 gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
661 {
662   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
663   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
664   GstStateChangeReturn ret;
665
666   switch (transition) {
667     case GST_STATE_CHANGE_NULL_TO_READY:
668       if (priv->enable_quirks)
669         gst_ks_video_src_apply_driver_quirks (self);
670       if (!gst_ks_video_src_start_worker (self))
671         goto open_failed;
672       break;
673     default:
674       break;
675   }
676
677   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
678
679   switch (transition) {
680     case GST_STATE_CHANGE_READY_TO_NULL:
681       gst_ks_video_src_stop_worker (self);
682       break;
683     default:
684       break;
685   }
686
687   return ret;
688
689   /* ERRORS */
690 open_failed:
691   {
692     gst_ks_video_src_stop_worker (self);
693     return GST_STATE_CHANGE_FAILURE;
694   }
695 }
696
697 static gboolean
698 gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
699 {
700   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
701   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
702
703   GST_OBJECT_LOCK (element);
704   if (clock != NULL && priv->ksclock != NULL)
705     gst_ks_clock_provide_master_clock (priv->ksclock, clock);
706   GST_OBJECT_UNLOCK (element);
707
708   return GST_ELEMENT_CLASS (parent_class)->set_clock (element, clock);
709 }
710
711 static GstCaps *
712 gst_ks_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
713 {
714   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
715   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
716
717   if (priv->device != NULL)
718     return gst_ks_video_device_get_available_caps (priv->device);
719   else
720     return NULL;                /* BaseSrc will return template caps */
721 }
722
723 static gboolean
724 gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
725 {
726   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
727   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
728
729   if (priv->device == NULL)
730     return FALSE;
731
732   KS_WORKER_LOCK (priv);
733   priv->worker_pending_caps = caps;
734   KS_WORKER_NOTIFY (priv);
735   while (priv->worker_pending_caps == caps)
736     KS_WORKER_WAIT_FOR_RESULT (priv);
737   KS_WORKER_UNLOCK (priv);
738
739   GST_DEBUG ("Result is %d", priv->worker_setcaps_result);
740   return priv->worker_setcaps_result;
741 }
742
743 static GstCaps *
744 gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
745 {
746   GstStructure *structure;
747   GstCaps *fixated_caps;
748   gint i;
749
750   fixated_caps = gst_caps_make_writable (caps);
751
752   for (i = 0; i < gst_caps_get_size (fixated_caps); ++i) {
753     structure = gst_caps_get_structure (fixated_caps, i);
754     gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
755     gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
756     gst_structure_fixate_field_nearest_fraction (structure, "framerate",
757         G_MAXINT, 1);
758   }
759
760   return gst_caps_fixate (fixated_caps);
761 }
762
763 static gboolean
764 gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
765 {
766   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
767   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
768   gboolean result = FALSE;
769
770   switch (GST_QUERY_TYPE (query)) {
771     case GST_QUERY_LATENCY:{
772       GstClockTime min_latency, max_latency;
773
774       if (priv->device == NULL)
775         goto beach;
776
777       result = gst_ks_video_device_get_latency (priv->device, &min_latency,
778           &max_latency);
779       if (!result)
780         goto beach;
781
782       GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
783           " max %" GST_TIME_FORMAT,
784           GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
785
786       gst_query_set_latency (query, TRUE, min_latency, max_latency);
787       break;
788     }
789     default:
790       result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
791       break;
792   }
793
794 beach:
795   return result;
796 }
797
798 static gboolean
799 gst_ks_video_src_unlock (GstBaseSrc * basesrc)
800 {
801   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
802   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
803
804   GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
805
806   gst_ks_video_device_cancel (priv->device);
807   return TRUE;
808 }
809
810 static gboolean
811 gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
812 {
813   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
814   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
815
816   GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
817
818   gst_ks_video_device_cancel_stop (priv->device);
819   return TRUE;
820 }
821
822 static gboolean
823 gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
824     GstClockTime presentation_time)
825 {
826   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
827   GstClockTime duration;
828   GstClock *clock;
829   GstClockTime timestamp, base_time;
830
831   /* Don't timestamp muxed streams */
832   if (gst_ks_video_device_stream_is_muxed (priv->device)) {
833     duration = timestamp = GST_CLOCK_TIME_NONE;
834     goto timestamp;
835   }
836
837   duration = gst_ks_video_device_get_duration (priv->device);
838
839   GST_OBJECT_LOCK (self);
840   clock = GST_ELEMENT_CLOCK (self);
841   if (clock != NULL) {
842     gst_object_ref (clock);
843     base_time = GST_ELEMENT (self)->base_time;
844   } else {
845     timestamp = GST_CLOCK_TIME_NONE;
846   }
847   GST_OBJECT_UNLOCK (self);
848
849   if (clock != NULL) {
850     /* The time according to the current clock */
851     timestamp = gst_clock_get_time (clock) - base_time;
852     if (timestamp > duration)
853       timestamp -= duration;
854     else
855       timestamp = 0;
856
857     gst_object_unref (clock);
858     clock = NULL;
859   }
860
861 timestamp:
862   GST_BUFFER_PTS (buf) = timestamp;
863   GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
864   GST_BUFFER_DURATION (buf) = duration;
865
866   return TRUE;
867 }
868
869 static void
870 gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
871 {
872   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
873   GstClock *clock;
874
875   GST_OBJECT_LOCK (self);
876   clock = GST_ELEMENT_CLOCK (self);
877   if (clock != NULL)
878     gst_object_ref (clock);
879   GST_OBJECT_UNLOCK (self);
880
881   if (clock != NULL) {
882     GstClockTime now = gst_clock_get_time (clock);
883     gst_object_unref (clock);
884
885     priv->count++;
886
887     if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
888       if (now - priv->last_sampling >= GST_SECOND) {
889         GST_OBJECT_LOCK (self);
890         priv->fps = priv->count;
891         GST_OBJECT_UNLOCK (self);
892
893         g_object_notify (G_OBJECT (self), "fps");
894
895         priv->last_sampling = now;
896         priv->count = 0;
897       }
898     } else {
899       priv->last_sampling = now;
900     }
901   }
902 }
903
904 static GstFlowReturn
905 gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
906 {
907   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
908   GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
909   GstFlowReturn result;
910   GstClockTime presentation_time;
911   gulong error_code;
912   gchar *error_str;
913
914   g_assert (priv->device != NULL);
915
916   if (!gst_ks_video_device_has_caps (priv->device))
917     goto error_no_caps;
918
919   if (G_UNLIKELY (!priv->running)) {
920     KS_WORKER_LOCK (priv);
921     priv->worker_pending_run = TRUE;
922     KS_WORKER_NOTIFY (priv);
923     while (priv->worker_pending_run)
924       KS_WORKER_WAIT_FOR_RESULT (priv);
925     priv->running = priv->worker_run_result;
926     error_code = priv->worker_error_code;
927     KS_WORKER_UNLOCK (priv);
928
929     if (!priv->running)
930       goto error_start_capture;
931   }
932
933   do {
934     if (*buf != NULL) {
935       gst_buffer_unref (*buf);
936       *buf = NULL;
937     }
938
939     result = gst_ks_video_device_read_frame (priv->device, buf,
940         &presentation_time, &error_code, &error_str);
941     if (G_UNLIKELY (result != GST_FLOW_OK))
942       goto error_read_frame;
943   }
944   while (!gst_ks_video_src_timestamp_buffer (self, *buf, presentation_time));
945
946   if (G_UNLIKELY (priv->do_stats))
947     gst_ks_video_src_update_statistics (self);
948
949   if (!gst_ks_video_device_postprocess_frame (priv->device, buf)) {
950     GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Postprocessing failed"),
951         ("Postprocessing failed"));
952     return GST_FLOW_ERROR;
953   }
954
955   return GST_FLOW_OK;
956
957   /* ERRORS */
958 error_no_caps:
959   {
960     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
961         ("not negotiated"), ("maybe setcaps failed?"));
962
963     return GST_FLOW_ERROR;
964   }
965 error_start_capture:
966   {
967     const gchar *debug_str = "failed to change pin state to KSSTATE_RUN";
968
969     switch (error_code) {
970       case ERROR_FILE_NOT_FOUND:
971         GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
972             ("failed to start capture (device unplugged)"), ("%s", debug_str));
973         break;
974       case ERROR_NO_SYSTEM_RESOURCES:
975         GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
976             ("failed to start capture (device already in use)"), ("%s",
977                 debug_str));
978         break;
979       default:
980         GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
981             ("failed to start capture (0x%08x)", (guint) error_code), ("%s",
982                 debug_str));
983         break;
984     }
985
986     return GST_FLOW_ERROR;
987   }
988 error_read_frame:
989   {
990     if (result == GST_FLOW_ERROR) {
991       if (error_str != NULL) {
992         GST_ELEMENT_ERROR (self, RESOURCE, READ,
993             ("read failed: %s [0x%08x]", error_str, (guint) error_code),
994             ("gst_ks_video_device_read_frame failed"));
995       }
996     } else if (result == GST_FLOW_CUSTOM_ERROR) {
997       GST_ELEMENT_ERROR (self, RESOURCE, READ,
998           ("read failed"), ("gst_ks_video_device_read_frame failed"));
999     }
1000
1001     g_free (error_str);
1002
1003     return result;
1004   }
1005 }
1006
1007 static GstBuffer *
1008 gst_ks_video_src_alloc_buffer (guint size, guint alignment, gpointer user_data)
1009 {
1010   GstKsVideoSrc *self = GST_KS_VIDEO_SRC (user_data);
1011   GstBuffer *buf;
1012   GstAllocationParams params = { 0, alignment - 1, 0, 0, };
1013
1014   buf = gst_buffer_new_allocate (NULL, size, &params);
1015   if (buf == NULL)
1016     goto error_alloc_buffer;
1017
1018   return buf;
1019
1020 error_alloc_buffer:
1021   {
1022     GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
1023
1024     return NULL;
1025   }
1026 }
1027
1028 static gboolean
1029 plugin_init (GstPlugin * plugin)
1030 {
1031   GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
1032       0, "Kernel streaming video source");
1033
1034   if (!gst_element_register (plugin, "ksvideosrc",
1035           GST_RANK_PRIMARY, GST_TYPE_KS_VIDEO_SRC))
1036     return FALSE;
1037
1038   if (!gst_device_provider_register (plugin, "ksdeviceprovider",
1039           GST_RANK_PRIMARY, GST_TYPE_KS_DEVICE_PROVIDER))
1040     return FALSE;
1041
1042   return TRUE;
1043 }
1044
1045 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1046     GST_VERSION_MINOR,
1047     winks,
1048     "Windows kernel streaming plugin",
1049     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)